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 KeyUp += new KeyEventHandler (OnKeyUpLB);
141 GotFocus += new EventHandler (OnGotFocus);
142 LostFocus += new EventHandler (OnLostFocus);
144 SetStyle (ControlStyles.UserPaint, false);
147 custom_tab_offsets = new IntegerCollection (this);
152 static object DrawItemEvent = new object ();
153 static object MeasureItemEvent = new object ();
154 static object SelectedIndexChangedEvent = new object ();
157 [EditorBrowsable (EditorBrowsableState.Never)]
158 public new event EventHandler BackgroundImageChanged {
159 add { base.BackgroundImageChanged += value; }
160 remove { base.BackgroundImageChanged -= value; }
165 [EditorBrowsable (EditorBrowsableState.Never)]
166 public new event EventHandler BackgroundImageLayoutChanged {
167 add { base.BackgroundImageLayoutChanged += value; }
168 remove { base.BackgroundImageLayoutChanged -= value; }
172 [EditorBrowsable (EditorBrowsableState.Always)]
175 [EditorBrowsable (EditorBrowsableState.Advanced)]
177 public new event EventHandler Click {
178 add { base.Click += value; }
179 remove { base.Click -= value; }
182 public event DrawItemEventHandler DrawItem {
183 add { Events.AddHandler (DrawItemEvent, value); }
184 remove { Events.RemoveHandler (DrawItemEvent, value); }
187 public event MeasureItemEventHandler MeasureItem {
188 add { Events.AddHandler (MeasureItemEvent, value); }
189 remove { Events.RemoveHandler (MeasureItemEvent, value); }
194 [EditorBrowsable (EditorBrowsableState.Always)]
195 public new event MouseEventHandler MouseClick {
196 add { base.MouseClick += value; }
197 remove { base.MouseClick -= value; }
201 [EditorBrowsable (EditorBrowsableState.Never)]
202 public new event EventHandler PaddingChanged {
203 add { base.PaddingChanged += value; }
204 remove { base.PaddingChanged -= value; }
209 [EditorBrowsable (EditorBrowsableState.Never)]
210 public new event PaintEventHandler Paint {
211 add { base.Paint += value; }
212 remove { base.Paint -= value; }
215 public event EventHandler SelectedIndexChanged {
216 add { Events.AddHandler (SelectedIndexChangedEvent, value); }
217 remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
221 [EditorBrowsable (EditorBrowsableState.Advanced)]
222 public new event EventHandler TextChanged {
223 add { base.TextChanged += value; }
224 remove { base.TextChanged -= value; }
228 #region UIA Framework Events
231 // We are using Reflection to add/remove internal events.
232 // Class ListProvider uses the events.
234 //Event used to generate UIA Selection Pattern
235 static object UIASelectionModeChangedEvent = new object ();
237 internal event EventHandler UIASelectionModeChanged {
238 add { Events.AddHandler (UIASelectionModeChangedEvent, value); }
239 remove { Events.RemoveHandler (UIASelectionModeChangedEvent, value); }
242 internal void OnUIASelectionModeChangedEvent ()
244 EventHandler eh = (EventHandler) Events [UIASelectionModeChangedEvent];
246 eh (this, EventArgs.Empty);
249 static object UIAFocusedItemChangedEvent = new object ();
251 internal event EventHandler UIAFocusedItemChanged {
252 add { Events.AddHandler (UIAFocusedItemChangedEvent, value); }
253 remove { Events.RemoveHandler (UIAFocusedItemChangedEvent, value); }
256 internal void OnUIAFocusedItemChangedEvent ()
258 EventHandler eh = (EventHandler) Events [UIAFocusedItemChangedEvent];
260 eh (this, EventArgs.Empty);
264 #endregion UIA Framework Events
266 #region Public Properties
267 public override Color BackColor {
268 get { return base.BackColor; }
270 if (base.BackColor == value)
273 base.BackColor = value;
274 base.Refresh (); // Careful. Calling the base method is not the same that calling
275 } // the overriden one that refresh also all the items
279 [EditorBrowsable (EditorBrowsableState.Never)]
280 public override Image BackgroundImage {
281 get { return base.BackgroundImage; }
283 base.BackgroundImage = value;
290 [EditorBrowsable (EditorBrowsableState.Never)]
291 public override ImageLayout BackgroundImageLayout {
292 get { return base.BackgroundImageLayout; }
293 set { base.BackgroundImageLayout = value; }
297 [DefaultValue (BorderStyle.Fixed3D)]
299 public BorderStyle BorderStyle {
300 get { return InternalBorderStyle; }
302 InternalBorderStyle = value;
303 UpdateListBoxBounds ();
309 public int ColumnWidth {
310 get { return column_width; }
313 throw new ArgumentException ("A value less than zero is assigned to the property.");
315 column_width = value;
318 ColumnWidthInternal = 120;
320 ColumnWidthInternal = value;
326 protected override CreateParams CreateParams {
327 get { return base.CreateParams;}
332 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
333 public IntegerCollection CustomTabOffsets {
334 get { return custom_tab_offsets; }
338 protected override Size DefaultSize {
339 get { return new Size (120, 96); }
342 [RefreshProperties(RefreshProperties.Repaint)]
343 [DefaultValue (DrawMode.Normal)]
344 public virtual DrawMode DrawMode {
345 get { return draw_mode; }
347 if (!Enum.IsDefined (typeof (DrawMode), value))
348 throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for DrawMode", value));
350 if (value == DrawMode.OwnerDrawVariable && multicolumn == true)
351 throw new ArgumentException ("Cannot have variable height and multicolumn");
353 if (draw_mode == value)
358 if (draw_mode == DrawMode.OwnerDrawVariable)
359 item_heights = new Hashtable ();
364 Parent.PerformLayout (this, "DrawMode");
370 public override Font Font {
371 get { return base.Font; }
372 set { base.Font = value; }
376 public override Color ForeColor {
377 get { return base.ForeColor; }
379 if (base.ForeColor == value)
382 base.ForeColor = value;
389 public int HorizontalExtent {
390 get { return horizontal_extent; }
392 if (horizontal_extent == value)
395 horizontal_extent = value;
400 [DefaultValue (false)]
402 public bool HorizontalScrollbar {
403 get { return horizontal_scrollbar; }
405 if (horizontal_scrollbar == value)
408 horizontal_scrollbar = value;
414 [DefaultValue (true)]
416 [RefreshProperties(RefreshProperties.Repaint)]
417 public bool IntegralHeight {
418 get { return integral_height; }
420 if (integral_height == value)
423 integral_height = value;
424 UpdateListBoxBounds ();
430 [RefreshProperties(RefreshProperties.Repaint)]
431 public virtual int ItemHeight {
433 if (item_height == -1) {
434 SizeF sz = TextRenderer.MeasureString ("The quick brown Fox", Font);
435 item_height = (int) sz.Height;
441 throw new ArgumentOutOfRangeException ("The ItemHeight property was set beyond 255 pixels");
443 explicit_item_height = true;
444 if (item_height == value)
449 UpdateListBoxBounds ();
454 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
456 [Editor ("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
458 [MergableProperty (false)]
460 public ObjectCollection Items {
461 get { return items; }
464 [DefaultValue (false)]
465 public bool MultiColumn {
466 get { return multicolumn; }
468 if (multicolumn == value)
471 if (value == true && DrawMode == DrawMode.OwnerDrawVariable)
472 throw new ArgumentException ("A multicolumn ListBox cannot have a variable-sized height.");
482 [EditorBrowsable (EditorBrowsableState.Never)]
483 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
484 public new Padding Padding {
485 get { return padding; }
486 set { padding = value; }
491 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
492 [EditorBrowsable (EditorBrowsableState.Advanced)]
493 public int PreferredHeight {
496 if (draw_mode == DrawMode.Normal)
497 itemsHeight = FontHeight * items.Count;
498 else if (draw_mode == DrawMode.OwnerDrawFixed)
499 itemsHeight = ItemHeight * items.Count;
500 else if (draw_mode == DrawMode.OwnerDrawVariable) {
501 for (int i = 0; i < items.Count; i++)
502 itemsHeight += (int) item_heights [Items [i]];
509 public override RightToLeft RightToLeft {
510 get { return base.RightToLeft; }
512 base.RightToLeft = value;
513 if (base.RightToLeft == RightToLeft.Yes)
514 StringFormat.Alignment = StringAlignment.Far;
516 StringFormat.Alignment = StringAlignment.Near;
521 // Only affects the Vertical ScrollBar
522 [DefaultValue (false)]
524 public bool ScrollAlwaysVisible {
525 get { return scroll_always_visible; }
527 if (scroll_always_visible == value)
530 scroll_always_visible = value;
537 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
538 public override int SelectedIndex {
540 if (selected_indices == null)
543 return selected_indices.Count > 0 ? selected_indices [0] : -1;
546 if (value < -1 || value >= Items.Count)
547 throw new ArgumentOutOfRangeException ("Index of out range");
549 if (SelectionMode == SelectionMode.None)
550 throw new ArgumentException ("cannot call this method if SelectionMode is SelectionMode.None");
553 selected_indices.Clear ();
555 selected_indices.Add (value);
560 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
561 public SelectedIndexCollection SelectedIndices {
562 get { return selected_indices; }
567 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
568 public object SelectedItem {
570 if (SelectedItems.Count > 0)
571 return SelectedItems[0];
576 if (value != null && !Items.Contains (value))
577 return; // FIXME: this is probably an exception
579 SelectedIndex = value == null ? - 1 : Items.IndexOf (value);
584 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
585 public SelectedObjectCollection SelectedItems {
586 get {return selected_items;}
589 [DefaultValue (SelectionMode.One)]
590 public virtual SelectionMode SelectionMode {
591 get { return selection_mode; }
593 if (!Enum.IsDefined (typeof (SelectionMode), value))
594 throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for SelectionMode", value));
596 if (selection_mode == value)
599 selection_mode = value;
601 switch (selection_mode) {
602 case SelectionMode.None:
603 SelectedIndices.Clear ();
606 case SelectionMode.One:
607 // FIXME: Probably this can be improved
608 ArrayList old_selection = (ArrayList) SelectedIndices.List.Clone ();
609 for (int i = 1; i < old_selection.Count; i++)
610 SelectedIndices.Remove ((int)old_selection [i]);
618 // UIA Framework: Generates SelectionModeChanged event.
619 OnUIASelectionModeChangedEvent ();
624 [DefaultValue (false)]
626 get { return sorted; }
639 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
640 [EditorBrowsable (EditorBrowsableState.Advanced)]
641 public override string Text {
643 if (SelectionMode != SelectionMode.None && SelectedIndex != -1)
644 return GetItemText (SelectedItem);
652 if (SelectionMode == SelectionMode.None)
657 index = FindStringExact (value);
662 SelectedIndex = index;
667 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
668 public int TopIndex {
669 get { return top_index; }
671 if (value == top_index)
674 if (value < 0 || value >= Items.Count)
677 int page_size = (items_area.Height / ItemHeight);
679 if (Items.Count < page_size)
681 else if (!multicolumn)
682 top_index = Math.Min (value, Items.Count - page_size);
693 [DefaultValue (false)]
694 public bool UseCustomTabOffsets {
695 get { return use_custom_tab_offsets; }
697 if (use_custom_tab_offsets != value) {
698 use_custom_tab_offsets = value;
699 CalculateTabStops ();
704 [DefaultValue (true)]
705 public bool UseTabStops {
706 get { return use_tabstops; }
708 if (use_tabstops == value)
711 use_tabstops = value;
712 CalculateTabStops ();
717 protected override bool AllowSelection {
719 return SelectionMode != SelectionMode.None;
724 #endregion Public Properties
726 #region Private Properties
728 private int ColumnWidthInternal {
729 get { return column_width_internal; }
730 set { column_width_internal = value; }
733 private int row_count = 1;
734 private int RowCount {
736 return MultiColumn ? row_count : Items.Count;
740 #endregion Private Properties
742 #region UIA Framework Properties
744 internal ScrollBar UIAHScrollBar {
745 get { return hscrollbar; }
748 internal ScrollBar UIAVScrollBar {
749 get { return vscrollbar; }
752 #endregion UIA Framework Properties
754 #region Public Methods
756 [Obsolete ("this method has been deprecated")]
758 protected virtual void AddItemsCore (object[] value)
760 Items.AddRange (value);
763 public void BeginUpdate ()
765 suspend_layout = true;
768 public void ClearSelected ()
770 selected_indices.Clear ();
773 protected virtual ObjectCollection CreateItemCollection ()
775 return new ObjectCollection (this);
778 public void EndUpdate ()
780 suspend_layout = false;
785 public int FindString (String s)
787 return FindString (s, -1);
790 public int FindString (string s, int startIndex)
792 if (Items.Count == 0)
793 return -1; // No exception throwing if empty
795 if (startIndex < -1 || startIndex >= Items.Count)
796 throw new ArgumentOutOfRangeException ("Index of out range");
798 startIndex = (startIndex == Items.Count - 1) ? 0 : startIndex + 1;
802 string text = GetItemText (Items [i]);
803 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (text, s,
804 CompareOptions.IgnoreCase))
807 i = (i == Items.Count - 1) ? 0 : i + 1;
815 public int FindStringExact (string s)
817 return FindStringExact (s, -1);
820 public int FindStringExact (string s, int startIndex)
822 if (Items.Count == 0)
823 return -1; // No exception throwing if empty
825 if (startIndex < -1 || startIndex >= Items.Count)
826 throw new ArgumentOutOfRangeException ("Index of out range");
828 startIndex = (startIndex + 1 == Items.Count) ? 0 : startIndex + 1;
832 if (String.Compare (GetItemText (Items[i]), s, true) == 0)
835 i = (i + 1 == Items.Count) ? 0 : i + 1;
843 public int GetItemHeight (int index)
845 if (index < 0 || index >= Items.Count)
846 throw new ArgumentOutOfRangeException ("Index of out range");
848 if (DrawMode == DrawMode.OwnerDrawVariable && IsHandleCreated == true) {
850 object o = Items [index];
851 if (item_heights.Contains (o))
852 return (int) item_heights [o];
854 MeasureItemEventArgs args = new MeasureItemEventArgs (DeviceContext, index, ItemHeight);
855 OnMeasureItem (args);
856 item_heights [o] = args.ItemHeight;
857 return args.ItemHeight;
863 public Rectangle GetItemRectangle (int index)
865 if (index < 0 || index >= Items.Count)
866 throw new ArgumentOutOfRangeException ("GetItemRectangle index out of range.");
868 Rectangle rect = new Rectangle ();
871 int col = index / RowCount;
873 if (y < 0) // We convert it to valid positive value
874 y += RowCount * (top_index / RowCount);
875 rect.Y = (y % RowCount) * ItemHeight;
876 rect.X = (col - (top_index / RowCount)) * ColumnWidthInternal;
877 rect.Height = ItemHeight;
878 rect.Width = ColumnWidthInternal;
881 rect.Height = GetItemHeight (index);
882 rect.Width = items_area.Width;
884 if (DrawMode == DrawMode.OwnerDrawVariable) {
886 if (index >= top_index) {
887 for (int i = top_index; i < index; i++) {
888 rect.Y += GetItemHeight (i);
891 for (int i = index; i < top_index; i++) {
892 rect.Y -= GetItemHeight (i);
896 rect.Y = ItemHeight * (index - top_index);
900 if (this is CheckedListBox)
907 [EditorBrowsable (EditorBrowsableState.Advanced)]
908 protected override Rectangle GetScaledBounds (Rectangle bounds, SizeF factor, BoundsSpecified specified)
910 bounds.Height = requested_height;
912 return base.GetScaledBounds (bounds, factor, specified);
916 public bool GetSelected (int index)
918 if (index < 0 || index >= Items.Count)
919 throw new ArgumentOutOfRangeException ("Index of out range");
921 return SelectedIndices.Contains (index);
924 public int IndexFromPoint (Point p)
926 return IndexFromPoint (p.X, p.Y);
929 // Only returns visible points
930 public int IndexFromPoint (int x, int y)
933 if (Items.Count == 0) {
937 for (int i = top_index; i <= last_visible_index; i++) {
938 if (GetItemRectangle (i).Contains (x,y) == true)
945 protected override void OnChangeUICues (UICuesEventArgs e)
947 base.OnChangeUICues (e);
950 protected override void OnDataSourceChanged (EventArgs e)
952 base.OnDataSourceChanged (e);
955 if (DataSource == null || DataManager == null) {
958 SelectedIndex = DataManager.Position;
962 protected override void OnDisplayMemberChanged (EventArgs e)
964 base.OnDisplayMemberChanged (e);
966 if (DataManager == null || !IsHandleCreated)
973 protected virtual void OnDrawItem (DrawItemEventArgs e)
976 case DrawMode.OwnerDrawFixed:
977 case DrawMode.OwnerDrawVariable:
978 DrawItemEventHandler eh = (DrawItemEventHandler)(Events [DrawItemEvent]);
985 ThemeEngine.Current.DrawListBoxItem (this, e);
990 protected override void OnFontChanged (EventArgs e)
992 base.OnFontChanged (e);
995 StringFormat.SetTabStops (0, new float [] {(float)(Font.Height * 3.7)});
997 if (explicit_item_height) {
1000 SizeF sz = TextRenderer.MeasureString ("The quick brown Fox", Font);
1001 item_height = (int) sz.Height;
1003 UpdateListBoxBounds ();
1008 protected override void OnHandleCreated (EventArgs e)
1010 base.OnHandleCreated (e);
1013 UpdateListBoxBounds ();
1016 EnsureVisible (focused_item);
1019 protected override void OnHandleDestroyed (EventArgs e)
1021 base.OnHandleDestroyed (e);
1024 protected virtual void OnMeasureItem (MeasureItemEventArgs e)
1026 if (draw_mode != DrawMode.OwnerDrawVariable)
1029 MeasureItemEventHandler eh = (MeasureItemEventHandler)(Events [MeasureItemEvent]);
1034 protected override void OnParentChanged (EventArgs e)
1036 base.OnParentChanged (e);
1039 protected override void OnResize (EventArgs e)
1042 if (canvas_size.IsEmpty || MultiColumn)
1048 protected override void OnSelectedIndexChanged (EventArgs e)
1050 base.OnSelectedIndexChanged (e);
1052 EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
1057 protected override void OnSelectedValueChanged (EventArgs e)
1059 base.OnSelectedValueChanged (e);
1062 public override void Refresh ()
1064 if (draw_mode == DrawMode.OwnerDrawVariable)
1065 item_heights.Clear ();
1070 protected override void RefreshItem (int index)
1072 if (index < 0 || index >= Items.Count)
1073 throw new ArgumentOutOfRangeException ("Index of out range");
1075 if (draw_mode == DrawMode.OwnerDrawVariable)
1076 item_heights.Remove (Items [index]);
1080 protected override void RefreshItems ()
1082 for (int i = 0; i < Items.Count; i++) {
1090 public override void ResetBackColor ()
1092 base.ResetBackColor ();
1095 public override void ResetForeColor ()
1097 base.ResetForeColor ();
1100 protected override void ScaleControl (SizeF factor, BoundsSpecified specified)
1102 base.ScaleControl (factor, specified);
1106 private int SnapHeightToIntegral (int height)
1110 switch (border_style) {
1111 case BorderStyle.Fixed3D:
1112 border = ThemeEngine.Current.Border3DSize.Height;
1114 case BorderStyle.FixedSingle:
1115 border = ThemeEngine.Current.BorderSize.Height;
1117 case BorderStyle.None:
1123 height -= (2 * border);
1124 height -= height % ItemHeight;
1125 height += (2 * border);
1130 protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
1132 if ((specified & BoundsSpecified.Height) == BoundsSpecified.Height)
1133 requested_height = height;
1135 if (IntegralHeight && IsHandleCreated)
1136 height = SnapHeightToIntegral (height);
1138 base.SetBoundsCore (x, y, width, height, specified);
1139 UpdateScrollBars ();
1140 last_visible_index = LastVisibleItem ();
1143 protected override void SetItemCore (int index, object value)
1145 if (index < 0 || index >= Items.Count)
1148 Items[index] = value;
1151 protected override void SetItemsCore (IList value)
1156 Items.AddItems (value);
1162 public void SetSelected (int index, bool value)
1164 if (index < 0 || index >= Items.Count)
1165 throw new ArgumentOutOfRangeException ("Index of out range");
1167 if (SelectionMode == SelectionMode.None)
1168 throw new InvalidOperationException ();
1171 SelectedIndices.Add (index);
1173 SelectedIndices.Remove (index);
1176 protected virtual void Sort ()
1182 // Sometimes we could need to Sort, and request a Refresh
1183 // in a different place, to not have the painting done twice
1185 void Sort (bool paint)
1187 if (Items.Count == 0)
1196 public override string ToString ()
1198 return base.ToString ();
1201 protected virtual void WmReflectCommand (ref Message m)
1205 protected override void WndProc (ref Message m)
1207 if ((Msg)m.Msg == Msg.WM_KEYDOWN) {
1208 if (ProcessKeyMessage (ref m))
1209 m.Result = IntPtr.Zero;
1211 HandleKeyDown ((Keys)m.WParam.ToInt32 ());
1218 base.WndProc (ref m);
1221 #endregion Public Methods
1223 #region Private Methods
1225 private void CalculateTabStops ()
1229 if (use_custom_tab_offsets) {
1230 float[] f = new float[custom_tab_offsets.Count];
1231 custom_tab_offsets.CopyTo (f, 0);
1232 StringFormat.SetTabStops (0, f);
1236 StringFormat.SetTabStops (0, new float[] { (float)(Font.Height * 3.7) });
1238 StringFormat.SetTabStops (0, new float[0]);
1243 private Size canvas_size;
1245 private void LayoutListBox ()
1247 if (!IsHandleCreated || suspend_layout)
1251 LayoutMultiColumn ();
1253 LayoutSingleColumn ();
1255 last_visible_index = LastVisibleItem ();
1256 UpdateScrollBars ();
1259 private void LayoutSingleColumn ()
1264 case DrawMode.OwnerDrawVariable:
1266 width = HorizontalExtent;
1267 for (int i = 0; i < Items.Count; i++) {
1268 height += GetItemHeight (i);
1272 case DrawMode.OwnerDrawFixed:
1273 height = Items.Count * ItemHeight;
1274 width = HorizontalExtent;
1277 case DrawMode.Normal:
1279 height = Items.Count * ItemHeight;
1281 for (int i = 0; i < Items.Count; i++) {
1282 SizeF sz = TextRenderer.MeasureString (GetItemText (Items[i]), Font);
1283 int t = (int)sz.Width;
1285 if (this is CheckedListBox)
1294 canvas_size = new Size (width, height);
1297 private void LayoutMultiColumn ()
1299 int usable_height = ClientRectangle.Height - (ScrollAlwaysVisible ? hscrollbar.Height : 0);
1300 row_count = Math.Max (1, usable_height / ItemHeight);
1302 int cols = (int) Math.Ceiling ((float)Items.Count / (float) row_count);
1303 Size sz = new Size (cols * ColumnWidthInternal, row_count * ItemHeight);
1304 if (!ScrollAlwaysVisible && sz.Width > ClientRectangle.Width && row_count > 1) {
1305 usable_height = ClientRectangle.Height - hscrollbar.Height;
1306 row_count = Math.Max (1, usable_height / ItemHeight);
1307 cols = (int) Math.Ceiling ((float)Items.Count / (float) row_count);
1308 sz = new Size (cols * ColumnWidthInternal, row_count * ItemHeight);
1313 internal void Draw (Rectangle clip, Graphics dc)
1315 Theme theme = ThemeEngine.Current;
1317 if (hscrollbar.Visible && vscrollbar.Visible) {
1318 // Paint the dead space in the bottom right corner
1319 Rectangle rect = new Rectangle (hscrollbar.Right, vscrollbar.Bottom, vscrollbar.Width, hscrollbar.Height);
1320 if (rect.IntersectsWith (clip))
1321 dc.FillRectangle (theme.ResPool.GetSolidBrush (theme.ColorControl), rect);
1324 dc.FillRectangle (theme.ResPool.GetSolidBrush (BackColor), items_area);
1326 if (Items.Count == 0)
1329 for (int i = top_index; i <= last_visible_index; i++) {
1330 Rectangle rect = GetItemDisplayRectangle (i, top_index);
1332 if (!clip.IntersectsWith (rect))
1335 DrawItemState state = DrawItemState.None;
1337 if (SelectedIndices.Contains (i))
1338 state |= DrawItemState.Selected;
1340 if (has_focus && FocusedItem == i)
1341 state |= DrawItemState.Focus;
1343 if (MultiColumn == false && hscrollbar != null && hscrollbar.Visible) {
1344 rect.X -= hscrollbar.Value;
1345 rect.Width += hscrollbar.Value;
1348 Color fore_color = (state & DrawItemState.Selected) != 0 ? ThemeEngine.Current.ColorHighlightText : ForeColor;
1349 OnDrawItem (new DrawItemEventArgs (dc, Font, rect, i, state, fore_color, BackColor));
1353 // Converts a GetItemRectangle to a one that we can display
1354 internal Rectangle GetItemDisplayRectangle (int index, int first_displayble)
1356 Rectangle item_rect;
1357 Rectangle first_item_rect = GetItemRectangle (first_displayble);
1358 item_rect = GetItemRectangle (index);
1359 item_rect.X -= first_item_rect.X;
1360 item_rect.Y -= first_item_rect.Y;
1362 // Subtract the checkboxes from the width
1363 if (this is CheckedListBox)
1364 item_rect.Width -= 14;
1370 private void HorizontalScrollEvent (object sender, EventArgs e)
1373 int top_item = top_index;
1374 int last_item = last_visible_index;
1376 top_index = RowCount * hscrollbar.Value;
1377 last_visible_index = LastVisibleItem ();
1379 if (top_item != top_index || last_item != last_visible_index)
1380 Invalidate (items_area);
1383 int old_offset = hbar_offset;
1384 hbar_offset = hscrollbar.Value;
1386 if (hbar_offset < 0)
1389 if (IsHandleCreated)
1390 XplatUI.ScrollWindow (Handle, items_area, old_offset - hbar_offset, 0, false);
1394 // Only returns visible points. The diference of with IndexFromPoint is that the rectangle
1395 // has screen coordinates
1396 private int IndexAtClientPoint (int x, int y)
1398 if (Items.Count == 0)
1403 else if (x > ClientRectangle.Right)
1404 x = ClientRectangle.Right;
1408 else if (y > ClientRectangle.Bottom)
1409 y = ClientRectangle.Bottom;
1411 for (int i = top_index; i <= last_visible_index; i++)
1412 if (GetItemDisplayRectangle (i, top_index).Contains (x, y))
1418 internal override bool IsInputCharInternal (char charCode)
1423 private int LastVisibleItem ()
1425 Rectangle item_rect;
1426 int top_y = items_area.Y + items_area.Height;
1429 if (top_index >= Items.Count)
1432 for (i = top_index; i < Items.Count; i++) {
1433 item_rect = GetItemDisplayRectangle (i, top_index);
1435 if (item_rect.X > items_area.Width)
1438 if (item_rect.Y + item_rect.Height > top_y)
1445 private void UpdateTopItem ()
1448 int col = top_index / RowCount;
1450 if (col > hscrollbar.Maximum)
1451 hscrollbar.Value = hscrollbar.Maximum;
1453 hscrollbar.Value = col;
1455 if (top_index > vscrollbar.Maximum)
1456 vscrollbar.Value = vscrollbar.Maximum;
1458 vscrollbar.Value = top_index;
1459 Scroll (vscrollbar, vscrollbar.Value - top_index);
1463 // Navigates to the indicated item and returns the new item
1464 private int NavigateItemVisually (ItemNavigation navigation)
1466 int page_size, columns, selected_index = -1;
1469 columns = items_area.Width / ColumnWidthInternal;
1470 page_size = columns * RowCount;
1471 if (page_size == 0) {
1472 page_size = RowCount;
1475 page_size = items_area.Height / ItemHeight;
1478 switch (navigation) {
1480 case ItemNavigation.PreviousColumn: {
1481 if (SelectedIndex - RowCount < 0) {
1485 if (SelectedIndex - RowCount < top_index) {
1486 top_index = SelectedIndex - RowCount;
1490 selected_index = SelectedIndex - RowCount;
1494 case ItemNavigation.NextColumn: {
1495 if (SelectedIndex + RowCount >= Items.Count) {
1499 if (SelectedIndex + RowCount > last_visible_index) {
1500 top_index = SelectedIndex;
1504 selected_index = SelectedIndex + RowCount;
1508 case ItemNavigation.First: {
1515 case ItemNavigation.Last: {
1517 int rows = items_area.Height / ItemHeight;
1520 selected_index = Items.Count - 1;
1523 if (Items.Count < rows) {
1525 selected_index = Items.Count - 1;
1528 top_index = Items.Count - rows;
1529 selected_index = Items.Count - 1;
1535 case ItemNavigation.Next: {
1536 if (FocusedItem == Items.Count - 1)
1540 selected_index = FocusedItem + 1;
1545 ArrayList heights = new ArrayList ();
1546 if (draw_mode == DrawMode.OwnerDrawVariable) {
1547 for (int i = top_index; i <= FocusedItem + 1; i++) {
1548 int h = GetItemHeight (i);
1553 bottom = ((FocusedItem + 1) - top_index + 1) * ItemHeight;
1556 if (bottom >= items_area.Height) {
1557 int overhang = bottom - items_area.Height;
1560 if (draw_mode == DrawMode.OwnerDrawVariable)
1561 while (overhang > 0)
1562 overhang -= (int) heights [offset];
1564 offset = (int) Math.Ceiling ((float)overhang / (float) ItemHeight);
1565 top_index += offset;
1568 selected_index = FocusedItem + 1;
1572 case ItemNavigation.Previous: {
1573 if (FocusedItem > 0) {
1574 if (FocusedItem - 1 < top_index) {
1578 selected_index = FocusedItem - 1;
1583 case ItemNavigation.NextPage: {
1584 if (Items.Count < page_size) {
1585 NavigateItemVisually (ItemNavigation.Last);
1589 if (FocusedItem + page_size - 1 >= Items.Count) {
1590 top_index = Items.Count - page_size;
1592 selected_index = Items.Count - 1;
1595 if (FocusedItem + page_size - 1 > last_visible_index) {
1596 top_index = FocusedItem;
1600 selected_index = FocusedItem + page_size - 1;
1606 case ItemNavigation.PreviousPage: {
1608 int rows = items_area.Height / ItemHeight;
1609 if (FocusedItem - (rows - 1) <= 0) {
1615 if (SelectedIndex - (rows - 1) < top_index) {
1616 top_index = FocusedItem - (rows - 1);
1620 selected_index = FocusedItem - (rows - 1);
1629 return selected_index;
1633 private void OnGotFocus (object sender, EventArgs e)
1635 if (Items.Count == 0)
1638 if (FocusedItem == -1)
1641 InvalidateItem (FocusedItem);
1644 private void OnLostFocus (object sender, EventArgs e)
1646 if (FocusedItem != -1)
1647 InvalidateItem (FocusedItem);
1650 private bool KeySearch (Keys key)
1652 char c = (char) key;
1653 if (!Char.IsLetterOrDigit (c))
1656 int idx = FindString (c.ToString (), SelectedIndex);
1657 if (idx != ListBox.NoMatches)
1658 SelectedIndex = idx;
1663 internal void HandleKeyDown (Keys key)
1667 if (Items.Count == 0)
1670 if (KeySearch (key))
1675 case Keys.ControlKey:
1676 ctrl_pressed = true;
1680 shift_pressed = true;
1684 new_item = NavigateItemVisually (ItemNavigation.First);
1688 new_item = NavigateItemVisually (ItemNavigation.Last);
1692 new_item = NavigateItemVisually (ItemNavigation.Previous);
1696 new_item = NavigateItemVisually (ItemNavigation.Next);
1700 new_item = NavigateItemVisually (ItemNavigation.PreviousPage);
1704 new_item = NavigateItemVisually (ItemNavigation.NextPage);
1708 if (multicolumn == true) {
1709 new_item = NavigateItemVisually (ItemNavigation.NextColumn);
1714 if (multicolumn == true) {
1715 new_item = NavigateItemVisually (ItemNavigation.PreviousColumn);
1720 if (selection_mode == SelectionMode.MultiSimple) {
1721 SelectedItemFromNavigation (FocusedItem);
1730 if (new_item != -1) {
1731 FocusedItem = new_item;
1733 if (selection_mode != SelectionMode.MultiSimple)
1734 SelectedItemFromNavigation (new_item);
1738 private void OnKeyUpLB (object sender, KeyEventArgs e)
1740 switch (e.KeyCode) {
1741 case Keys.ControlKey:
1742 ctrl_pressed = false;
1745 shift_pressed = false;
1752 internal void InvalidateItem (int index)
1754 if (!IsHandleCreated)
1756 Rectangle bounds = GetItemDisplayRectangle (index, top_index);
1757 if (ClientRectangle.IntersectsWith (bounds))
1758 Invalidate (bounds);
1761 internal virtual void OnItemClick (int index)
1763 OnSelectedIndexChanged (EventArgs.Empty);
1764 OnSelectedValueChanged (EventArgs.Empty);
1768 int[] prev_selection;
1769 bool button_pressed = false;
1770 Point button_pressed_loc = new Point (-1, -1);
1772 private void SelectExtended (int index)
1776 ArrayList new_selection = new ArrayList ();
1777 int start = anchor < index ? anchor : index;
1778 int end = anchor > index ? anchor : index;
1779 for (int i = start; i <= end; i++)
1780 new_selection.Add (i);
1783 foreach (int i in prev_selection)
1784 if (!new_selection.Contains (i))
1785 new_selection.Add (i);
1787 // Need to make a copy since we can't enumerate and modify the collection
1789 ArrayList sel_indices = (ArrayList) selected_indices.List.Clone ();
1790 foreach (int i in sel_indices)
1791 if (!new_selection.Contains (i))
1792 selected_indices.Remove (i);
1794 foreach (int i in new_selection)
1795 if (!sel_indices.Contains (i))
1796 selected_indices.AddCore (i);
1800 private void OnMouseDownLB (object sender, MouseEventArgs e)
1802 // Only do stuff with the left mouse button
1803 if ((e.Button & MouseButtons.Left) == 0)
1806 int index = IndexAtClientPoint (e.X, e.Y);
1810 switch (SelectionMode) {
1811 case SelectionMode.One:
1812 SelectedIndices.AddCore (index); // Unselects previous one
1815 case SelectionMode.MultiSimple:
1816 if (SelectedIndices.Contains (index))
1817 SelectedIndices.RemoveCore (index);
1819 SelectedIndices.AddCore (index);
1822 case SelectionMode.MultiExtended:
1823 shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
1824 ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
1826 if (shift_pressed) {
1827 SelectedIndices.ClearCore ();
1828 SelectExtended (index);
1835 prev_selection = new int [SelectedIndices.Count];
1836 SelectedIndices.CopyTo (prev_selection, 0);
1838 if (SelectedIndices.Contains (index))
1839 SelectedIndices.RemoveCore (index);
1841 SelectedIndices.AddCore (index);
1846 SelectedIndices.ClearCore ();
1847 SelectedIndices.AddCore (index);
1850 case SelectionMode.None:
1856 button_pressed = true;
1857 button_pressed_loc = new Point (e.X, e.Y);
1858 FocusedItem = index;
1861 private void OnMouseMoveLB (object sender, MouseEventArgs e)
1863 // Don't take into account MouseMove events generated with MouseDown
1864 if (!button_pressed || button_pressed_loc == new Point (e.X, e.Y))
1867 int index = IndexAtClientPoint (e.X, e.Y);
1871 switch (SelectionMode) {
1872 case SelectionMode.One:
1873 SelectedIndices.AddCore (index); // Unselects previous one
1876 case SelectionMode.MultiSimple:
1879 case SelectionMode.MultiExtended:
1880 SelectExtended (index);
1883 case SelectionMode.None:
1889 FocusedItem = index;
1892 internal override void OnDragDropEnd (DragDropEffects effects)
1894 // In the case of a DnD operation (started on MouseDown)
1895 // there will be no MouseUp event, so we need to reset
1897 button_pressed = false;
1900 private void OnMouseUpLB (object sender, MouseEventArgs e)
1902 // Only do stuff with the left mouse button
1903 if ((e.Button & MouseButtons.Left) == 0)
1907 OnDoubleClick (EventArgs.Empty);
1909 OnMouseDoubleClick (e);
1912 else if (e.Clicks == 1) {
1913 OnClick (EventArgs.Empty);
1919 if (!button_pressed)
1922 int index = IndexAtClientPoint (e.X, e.Y);
1923 OnItemClick (index);
1924 button_pressed = ctrl_pressed = shift_pressed = false;
1927 private void Scroll (ScrollBar scrollbar, int delta)
1929 if (delta == 0 || !scrollbar.Visible || !scrollbar.Enabled)
1933 if (scrollbar == hscrollbar)
1934 max = hscrollbar.Maximum - (items_area.Width / ColumnWidthInternal) + 1;
1936 max = vscrollbar.Maximum - (items_area.Height / ItemHeight) + 1;
1938 int val = scrollbar.Value + delta;
1941 else if (val < scrollbar.Minimum)
1942 val = scrollbar.Minimum;
1943 scrollbar.Value = val;
1946 private void OnMouseWheelLB (object sender, MouseEventArgs me)
1948 if (Items.Count == 0)
1951 int lines = me.Delta / 120;
1954 Scroll (hscrollbar, -SystemInformation.MouseWheelScrollLines * lines);
1956 Scroll (vscrollbar, -lines);
1959 internal override void OnPaintInternal (PaintEventArgs pevent)
1964 Draw (pevent.ClipRectangle, pevent.Graphics);
1967 internal void RepositionScrollBars ()
1969 if (vscrollbar.is_visible) {
1970 vscrollbar.Size = new Size (vscrollbar.Width, items_area.Height);
1971 vscrollbar.Location = new Point (items_area.Width, 0);
1974 if (hscrollbar.is_visible) {
1975 hscrollbar.Size = new Size (items_area.Width, hscrollbar.Height);
1976 hscrollbar.Location = new Point (0, items_area.Height);
1980 // An item navigation operation (mouse or keyboard) has caused to select a new item
1981 internal void SelectedItemFromNavigation (int index)
1983 switch (SelectionMode) {
1984 case SelectionMode.None:
1985 // .Net doesn't select the item, only ensures that it's visible
1986 // and fires the selection related events
1987 EnsureVisible (index);
1988 OnSelectedIndexChanged (EventArgs.Empty);
1989 OnSelectedValueChanged (EventArgs.Empty);
1991 case SelectionMode.One: {
1992 SelectedIndex = index;
1995 case SelectionMode.MultiSimple: {
1996 if (SelectedIndex == -1) {
1997 SelectedIndex = index;
2000 if (SelectedIndices.Contains (index))
2001 SelectedIndices.Remove (index);
2003 SelectedIndices.AddCore (index);
2005 OnSelectedIndexChanged (EventArgs.Empty);
2006 OnSelectedValueChanged (EventArgs.Empty);
2012 case SelectionMode.MultiExtended: {
2013 if (SelectedIndex == -1) {
2014 SelectedIndex = index;
2017 if (ctrl_pressed == false && shift_pressed == false) {
2018 SelectedIndices.Clear ();
2021 if (shift_pressed == true) {
2022 ShiftSelection (index);
2023 } else { // ctrl_pressed or single item
2024 SelectedIndices.AddCore (index);
2028 OnSelectedIndexChanged (EventArgs.Empty);
2029 OnSelectedValueChanged (EventArgs.Empty);
2039 private void ShiftSelection (int index)
2041 int shorter_item = -1, dist = Items.Count + 1, cur_dist;
2043 foreach (int idx in selected_indices) {
2045 cur_dist = idx - index;
2047 cur_dist = index - idx;
2050 if (cur_dist < dist) {
2056 if (shorter_item != -1) {
2059 if (shorter_item > index) {
2063 start = shorter_item;
2067 selected_indices.Clear ();
2068 for (int idx = start; idx <= end; idx++) {
2069 selected_indices.AddCore (idx);
2074 internal int FocusedItem {
2075 get { return focused_item; }
2077 if (focused_item == value)
2080 int prev = focused_item;
2082 focused_item = value;
2084 if (has_focus == false)
2088 InvalidateItem (prev);
2091 InvalidateItem (value);
2094 // UIA Framework: Generates FocusedItemChanged event.
2095 OnUIAFocusedItemChangedEvent ();
2100 StringFormat string_format;
2101 internal StringFormat StringFormat {
2103 if (string_format == null) {
2104 string_format = new StringFormat ();
2105 string_format.FormatFlags = StringFormatFlags.NoWrap;
2107 if (RightToLeft == RightToLeft.Yes)
2108 string_format.Alignment = StringAlignment.Far;
2110 string_format.Alignment = StringAlignment.Near;
2111 CalculateTabStops ();
2113 return string_format;
2117 internal virtual void CollectionChanged ()
2122 if (Items.Count == 0) {
2123 selected_indices.List.Clear ();
2127 if (Items.Count <= focused_item)
2128 focused_item = Items.Count - 1;
2130 if (!IsHandleCreated || suspend_layout)
2138 void EnsureVisible (int index)
2140 if (!IsHandleCreated || index == -1)
2143 if (index < top_index) {
2147 } else if (!multicolumn) {
2148 int rows = items_area.Height / ItemHeight;
2149 if (index >= (top_index + rows))
2150 top_index = index - rows + 1;
2154 int rows = Math.Max (1, items_area.Height / ItemHeight);
2155 int cols = Math.Max (1, items_area.Width / ColumnWidthInternal);
2157 if (index >= (top_index + (rows * cols))) {
2158 int incolumn = index / rows;
2159 top_index = (incolumn - (cols - 1)) * rows;
2167 private void UpdateListBoxBounds ()
2169 if (IsHandleCreated)
2170 SetBoundsInternal (bounds.X, bounds.Y, bounds.Width, IntegralHeight ? SnapHeightToIntegral (requested_height) : requested_height, BoundsSpecified.None);
2173 private void UpdateScrollBars ()
2175 items_area = ClientRectangle;
2176 if (UpdateHorizontalScrollBar ()) {
2177 items_area.Height -= hscrollbar.Height;
2178 if (UpdateVerticalScrollBar ()) {
2179 items_area.Width -= vscrollbar.Width;
2180 UpdateHorizontalScrollBar ();
2182 } else if (UpdateVerticalScrollBar ()) {
2183 items_area.Width -= vscrollbar.Width;
2184 if (UpdateHorizontalScrollBar ()) {
2185 items_area.Height -= hscrollbar.Height;
2186 UpdateVerticalScrollBar ();
2190 RepositionScrollBars ();
2193 /* Determines if the horizontal scrollbar has to be displyed */
2194 private bool UpdateHorizontalScrollBar ()
2197 bool enabled = true;
2200 if (canvas_size.Width > items_area.Width) {
2202 hscrollbar.Maximum = canvas_size.Width / ColumnWidthInternal - 1;
2203 } else if (ScrollAlwaysVisible == true) {
2206 hscrollbar.Maximum = 0;
2208 } else if (canvas_size.Width > ClientRectangle.Width && HorizontalScrollbar) {
2210 hscrollbar.Maximum = canvas_size.Width;
2211 hscrollbar.LargeChange = Math.Max (0, items_area.Width);
2212 } else if (scroll_always_visible && horizontal_scrollbar) {
2215 hscrollbar.Maximum = 0;
2218 hbar_offset = hscrollbar.Value;
2219 hscrollbar.Enabled = enabled;
2220 hscrollbar.Visible = show;
2225 /* Determines if the vertical scrollbar has to be displyed */
2226 private bool UpdateVerticalScrollBar ()
2228 if (MultiColumn || (Items.Count == 0 && !scroll_always_visible)) {
2229 vscrollbar.Visible = false;
2231 } else if (Items.Count == 0) {
2232 vscrollbar.Visible = true;
2233 vscrollbar.Enabled = false;
2234 vscrollbar.Maximum = 0;
2239 bool enabled = true;
2240 if (canvas_size.Height > items_area.Height) {
2242 vscrollbar.Maximum = Items.Count - 1;
2243 vscrollbar.LargeChange = Math.Max (items_area.Height / ItemHeight, 0);
2244 } else if (ScrollAlwaysVisible) {
2247 vscrollbar.Maximum = 0;
2250 vscrollbar.Enabled = enabled;
2251 vscrollbar.Visible = show;
2257 private void VerticalScrollEvent (object sender, EventArgs e)
2259 int top_item = top_index;
2261 top_index = /*row_count + */ vscrollbar.Value;
2262 last_visible_index = LastVisibleItem ();
2264 int delta = (top_item - top_index) * ItemHeight;
2265 if (DrawMode == DrawMode.OwnerDrawVariable) {
2268 if (top_index < top_item)
2269 for (int i = top_index; i < top_item; i++)
2270 delta += GetItemHeight (i);
2272 for (int i = top_item; i < top_index; i++)
2273 delta -= GetItemHeight (i);
2276 if (IsHandleCreated)
2277 XplatUI.ScrollWindow (Handle, items_area, 0, delta, false);
2280 #endregion Private Methods
2283 public class IntegerCollection : IList, ICollection, IEnumerable
2285 private ListBox owner;
2286 private List<int> list;
2288 #region Public Constructor
2289 public IntegerCollection (ListBox owner)
2292 list = new List<int> ();
2296 #region Public Properties
2299 get { return list.Count; }
2302 public int this [int index] {
2303 get { return list[index]; }
2304 set { list[index] = value; owner.CalculateTabStops (); }
2308 #region Public Methods
2309 public int Add (int item)
2311 // This collection does not allow duplicates
2312 if (!list.Contains (item)) {
2315 owner.CalculateTabStops ();
2318 return list.IndexOf (item);
2321 public void AddRange (int[] items)
2326 public void AddRange (IntegerCollection value)
2331 void AddItems (IList items)
2334 throw new ArgumentNullException ("items");
2336 foreach (int i in items)
2337 if (!list.Contains (i))
2343 public void Clear ()
2346 owner.CalculateTabStops ();
2349 public bool Contains (int item)
2351 return list.Contains (item);
2354 public void CopyTo (Array destination, int index)
2356 for (int i = 0; i < list.Count; i++)
2357 destination.SetValue (list[i], index++);
2360 public int IndexOf (int item)
2362 return list.IndexOf (item);
2365 public void Remove (int item)
2369 owner.CalculateTabStops ();
2372 public void RemoveAt (int index)
2375 throw new IndexOutOfRangeException ();
2377 list.RemoveAt (index);
2379 owner.CalculateTabStops ();
2383 #region IEnumerable Members
2384 IEnumerator IEnumerable.GetEnumerator ()
2386 return list.GetEnumerator ();
2390 #region IList Members
2391 int IList.Add (object item)
2393 int? intValue = item as int?;
2394 if (!intValue.HasValue)
2395 throw new ArgumentException ("item");
2396 return Add (intValue.Value);
2404 bool IList.Contains (object item)
2406 int? intValue = item as int?;
2407 if (!intValue.HasValue)
2409 return Contains (intValue.Value);
2412 int IList.IndexOf (object item)
2414 int? intValue = item as int?;
2415 if (!intValue.HasValue)
2417 return IndexOf (intValue.Value);
2420 void IList.Insert (int index, object value)
2422 throw new NotSupportedException (string.Format (
2423 CultureInfo.InvariantCulture, "No items "
2424 + "can be inserted into {0}, since it is"
2425 + " a sorted collection.", this.GetType ()));
2428 bool IList.IsFixedSize
2430 get { return false; }
2433 bool IList.IsReadOnly
2435 get { return false; }
2438 void IList.Remove (object value)
2440 int? intValue = value as int?;
2441 if (!intValue.HasValue)
2442 throw new ArgumentException ("value");
2444 Remove (intValue.Value);
2447 void IList.RemoveAt (int index)
2452 object IList.this[int index] {
2453 get { return this[index]; }
2454 set { this[index] = (int)value; }
2458 #region ICollection Members
2459 bool ICollection.IsSynchronized {
2460 get { return true; }
2463 object ICollection.SyncRoot {
2464 get { return this; }
2470 [ListBindable (false)]
2471 public class ObjectCollection : IList, ICollection, IEnumerable
2473 internal class ListObjectComparer : IComparer
2475 public int Compare (object a, object b)
2477 string str1 = a.ToString ();
2478 string str2 = b.ToString ();
2479 return str1.CompareTo (str2);
2483 private ListBox owner;
2484 internal ArrayList object_items = new ArrayList ();
2486 #region UIA Framework Events
2489 // We are using Reflection to add/remove internal events.
2490 // Class ListProvider uses the events.
2492 //Event used to generate UIA StructureChangedEvent
2493 static object UIACollectionChangedEvent = new object ();
2495 internal event CollectionChangeEventHandler UIACollectionChanged {
2496 add { owner.Events.AddHandler (UIACollectionChangedEvent, value); }
2497 remove { owner.Events.RemoveHandler (UIACollectionChangedEvent, value); }
2500 internal void OnUIACollectionChangedEvent (CollectionChangeEventArgs args)
2502 CollectionChangeEventHandler eh
2503 = (CollectionChangeEventHandler) owner.Events [UIACollectionChangedEvent];
2509 #endregion UIA Framework Events
2511 public ObjectCollection (ListBox owner)
2516 public ObjectCollection (ListBox owner, object[] value)
2522 public ObjectCollection (ListBox owner, ObjectCollection value)
2528 #region Public Properties
2530 get { return object_items.Count; }
2533 public bool IsReadOnly {
2534 get { return false; }
2538 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2539 public virtual object this [int index] {
2541 if (index < 0 || index >= Count)
2542 throw new ArgumentOutOfRangeException ("Index of out range");
2544 return object_items[index];
2547 if (index < 0 || index >= Count)
2548 throw new ArgumentOutOfRangeException ("Index of out range");
2550 throw new ArgumentNullException ("value");
2553 //UIA Framework event: Item Removed
2554 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, object_items [index]));
2557 object_items[index] = value;
2560 //UIA Framework event: Item Added
2561 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, value));
2564 owner.CollectionChanged ();
2568 bool ICollection.IsSynchronized {
2569 get { return false; }
2572 object ICollection.SyncRoot {
2573 get { return this; }
2576 bool IList.IsFixedSize {
2577 get { return false; }
2580 #endregion Public Properties
2582 #region Public Methods
2583 public int Add (object item)
2587 idx = AddItem (item);
2588 owner.CollectionChanged ();
2590 // If we are sorted, the item probably moved indexes, get the real one
2592 return this.IndexOf (item);
2597 public void AddRange (object[] items)
2602 public void AddRange (ObjectCollection value)
2607 internal void AddItems (IList items)
2610 throw new ArgumentNullException ("items");
2613 foreach (object mi in items)
2615 throw new ArgumentNullException ("item");
2618 foreach (object mi in items)
2621 owner.CollectionChanged ();
2624 public virtual void Clear ()
2626 owner.selected_indices.ClearCore ();
2627 object_items.Clear ();
2628 owner.CollectionChanged ();
2631 //UIA Framework event: Items list cleared
2632 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, null));
2636 public bool Contains (object value)
2639 throw new ArgumentNullException ("value");
2641 return object_items.Contains (value);
2645 public void CopyTo (object[] destination, int arrayIndex)
2647 object [] dest = destination;
2649 public void CopyTo (object [] dest, int arrayIndex)
2652 object_items.CopyTo (dest, arrayIndex);
2656 void ICollection.CopyTo (Array destination, int index)
2658 Array dest = destination;
2660 void ICollection.CopyTo (Array dest, int index)
2663 object_items.CopyTo (dest, index);
2666 public IEnumerator GetEnumerator ()
2668 return object_items.GetEnumerator ();
2671 int IList.Add (object item)
2676 public int IndexOf (object value)
2679 throw new ArgumentNullException ("value");
2681 return object_items.IndexOf (value);
2684 public void Insert (int index, object item)
2686 if (index < 0 || index > Count)
2687 throw new ArgumentOutOfRangeException ("Index of out range");
2689 throw new ArgumentNullException ("item");
2691 owner.BeginUpdate ();
2692 object_items.Insert (index, item);
2693 owner.CollectionChanged ();
2697 //UIA Framework event: Item Added
2698 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, item));
2702 public void Remove (object value)
2707 int index = IndexOf (value);
2712 public void RemoveAt (int index)
2714 if (index < 0 || index >= Count)
2715 throw new ArgumentOutOfRangeException ("Index of out range");
2719 //UIA Framework element removed
2720 object removed = object_items [index];
2722 UpdateSelection (index);
2723 object_items.RemoveAt (index);
2724 owner.CollectionChanged ();
2727 //UIA Framework event: Item Removed
2728 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, removed));
2731 #endregion Public Methods
2733 #region Private Methods
2734 internal int AddItem (object item)
2737 throw new ArgumentNullException ("item");
2739 int cnt = object_items.Count;
2740 object_items.Add (item);
2743 //UIA Framework event: Item Added
2744 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, item));
2750 // we receive the index to be removed
2751 void UpdateSelection (int removed_index)
2753 owner.selected_indices.Remove (removed_index);
2755 if (owner.selection_mode != SelectionMode.None) {
2756 int last_idx = object_items.Count - 1;
2758 // if the last item was selected, remove it from selection,
2759 // since it will become invalid after the removal
2760 if (owner.selected_indices.Contains (last_idx)) {
2761 owner.selected_indices.Remove (last_idx);
2763 // in SelectionMode.One try to put the selection on the new last item
2764 int new_idx = last_idx - 1;
2765 if (owner.selection_mode == SelectionMode.One && new_idx > -1)
2766 owner.selected_indices.Add (new_idx);
2772 internal void Sort ()
2774 object_items.Sort (new ListObjectComparer ());
2777 #endregion Private Methods
2780 public class SelectedIndexCollection : IList, ICollection, IEnumerable
2782 private ListBox owner;
2783 ArrayList selection;
2784 bool sorting_needed; // Selection state retrieval is done sorted - we do it lazyly
2786 #region UIA Framework Events
2789 // We are using Reflection to add/remove internal events.
2790 // Class ListProvider uses the events.
2792 //Event used to generate UIA StructureChangedEvent
2793 static object UIACollectionChangedEvent = new object ();
2795 internal event CollectionChangeEventHandler UIACollectionChanged {
2796 add { owner.Events.AddHandler (UIACollectionChangedEvent, value); }
2797 remove { owner.Events.RemoveHandler (UIACollectionChangedEvent, value); }
2800 internal void OnUIACollectionChangedEvent (CollectionChangeEventArgs args)
2802 CollectionChangeEventHandler eh
2803 = (CollectionChangeEventHandler) owner.Events [UIACollectionChangedEvent];
2809 #endregion UIA Framework Events
2812 public SelectedIndexCollection (ListBox owner)
2815 selection = new ArrayList ();
2818 #region Public Properties
2821 get { return selection.Count; }
2824 public bool IsReadOnly {
2825 get { return true; }
2828 public int this [int index] {
2830 if (index < 0 || index >= Count)
2831 throw new ArgumentOutOfRangeException ("Index of out range");
2834 return (int)selection [index];
2838 bool ICollection.IsSynchronized {
2839 get { return true; }
2842 bool IList.IsFixedSize{
2843 get { return true; }
2846 object ICollection.SyncRoot {
2847 get { return selection; }
2850 #endregion Public Properties
2852 #region Public Methods
2858 void Add (int index)
2860 if (AddCore (index)) {
2861 owner.OnSelectedIndexChanged (EventArgs.Empty);
2862 owner.OnSelectedValueChanged (EventArgs.Empty);
2866 // Need to separate selection logic from events,
2867 // since selection changes using keys/mouse handle them their own way
2868 internal bool AddCore (int index)
2870 if (selection.Contains (index))
2873 if (index == -1) // Weird MS behaviour
2875 if (index < -1 || index >= owner.Items.Count)
2876 throw new ArgumentOutOfRangeException ("index");
2877 if (owner.selection_mode == SelectionMode.None)
2878 throw new InvalidOperationException ("Cannot call this method when selection mode is SelectionMode.None");
2880 if (owner.selection_mode == SelectionMode.One && Count > 0) // Unselect previously selected item
2881 RemoveCore ((int)selection [0]);
2883 selection.Add (index);
2884 sorting_needed = true;
2885 owner.EnsureVisible (index);
2886 owner.FocusedItem = index;
2887 owner.InvalidateItem (index);
2889 // UIA Framework event: Selected item added
2890 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, index));
2904 owner.OnSelectedIndexChanged (EventArgs.Empty);
2905 owner.OnSelectedValueChanged (EventArgs.Empty);
2909 internal bool ClearCore ()
2911 if (selection.Count == 0)
2914 foreach (int index in selection)
2915 owner.InvalidateItem (index);
2919 // UIA Framework event: Selected items list updated
2920 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, -1));
2925 public bool Contains (int selectedIndex)
2927 foreach (int index in selection)
2928 if (index == selectedIndex)
2934 public void CopyTo (Array destination, int index)
2936 Array dest = destination;
2938 public void CopyTo (Array dest, int index)
2942 selection.CopyTo (dest, index);
2945 public IEnumerator GetEnumerator ()
2948 return selection.GetEnumerator ();
2951 // FIXME: Probably we can avoid sorting when calling
2952 // IndexOf (imagine a scenario where multiple removal of items
2959 void Remove (int index)
2961 // Separate logic from events here too
2962 if (RemoveCore (index)) {
2963 owner.OnSelectedIndexChanged (EventArgs.Empty);
2964 owner.OnSelectedValueChanged (EventArgs.Empty);
2968 internal bool RemoveCore (int index)
2970 int idx = IndexOf (index);
2974 selection.RemoveAt (idx);
2975 owner.InvalidateItem (index);
2978 // UIA Framework event: Selected item removed from selection
2979 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, index));
2986 int IList.Add (object value)
2988 throw new NotSupportedException ();
2993 throw new NotSupportedException ();
2996 bool IList.Contains (object selectedIndex)
2998 return Contains ((int)selectedIndex);
3001 int IList.IndexOf (object selectedIndex)
3003 return IndexOf ((int) selectedIndex);
3006 void IList.Insert (int index, object value)
3008 throw new NotSupportedException ();
3011 void IList.Remove (object value)
3013 throw new NotSupportedException ();
3016 void IList.RemoveAt (int index)
3018 throw new NotSupportedException ();
3021 object IList.this[int index]{
3022 get { return this [index]; }
3023 set {throw new NotImplementedException (); }
3026 public int IndexOf (int selectedIndex)
3030 for (int i = 0; i < selection.Count; i++)
3031 if ((int)selection [i] == selectedIndex)
3036 #endregion Public Methods
3037 internal ArrayList List {
3046 if (sorting_needed) {
3047 sorting_needed = false;
3053 public class SelectedObjectCollection : IList, ICollection, IEnumerable
3055 private ListBox owner;
3057 public SelectedObjectCollection (ListBox owner)
3062 #region Public Properties
3064 get { return owner.selected_indices.Count; }
3067 public bool IsReadOnly {
3068 get { return true; }
3072 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
3073 public object this [int index] {
3075 if (index < 0 || index >= Count)
3076 throw new ArgumentOutOfRangeException ("Index of out range");
3078 return owner.items [owner.selected_indices [index]];
3080 set {throw new NotSupportedException ();}
3083 bool ICollection.IsSynchronized {
3084 get { return true; }
3087 object ICollection.SyncRoot {
3088 get { return this; }
3091 bool IList.IsFixedSize {
3092 get { return true; }
3095 #endregion Public Properties
3097 #region Public Methods
3099 public void Add (object value)
3101 if (owner.selection_mode == SelectionMode.None)
3102 throw new ArgumentException ("Cannot call this method if SelectionMode is SelectionMode.None");
3104 int idx = owner.items.IndexOf (value);
3108 owner.selected_indices.Add (idx);
3111 public void Clear ()
3113 owner.selected_indices.Clear ();
3117 public bool Contains (object selectedObject)
3119 int idx = owner.items.IndexOf (selectedObject);
3120 return idx == -1 ? false : owner.selected_indices.Contains (idx);
3124 public void CopyTo (Array destination, int index)
3126 Array dest = destination;
3128 public void CopyTo (Array dest, int index)
3131 for (int i = 0; i < Count; i++)
3132 dest.SetValue (this [i], index++);
3136 public void Remove (object value)
3141 int idx = owner.items.IndexOf (value);
3145 owner.selected_indices.Remove (idx);
3149 int IList.Add (object value)
3151 throw new NotSupportedException ();
3156 throw new NotSupportedException ();
3159 void IList.Insert (int index, object value)
3161 throw new NotSupportedException ();
3164 void IList.Remove (object value)
3166 throw new NotSupportedException ();
3169 void IList.RemoveAt (int index)
3171 throw new NotSupportedException ();
3174 public int IndexOf (object selectedObject)
3176 int idx = owner.items.IndexOf (selectedObject);
3177 return idx == -1 ? -1 : owner.selected_indices.IndexOf (idx);
3180 public IEnumerator GetEnumerator ()
3182 //FIXME: write an enumerator that uses selection.GetEnumerator
3183 // so that invalidation is write on selection changes
3184 object [] items = new object [Count];
3185 for (int i = 0; i < Count; i++) {
3186 items [i] = owner.items [owner.selected_indices [i]];
3189 return items.GetEnumerator ();
3192 #endregion Public Methods