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;
38 using System.Collections.Generic;
40 namespace System.Windows.Forms
42 [DefaultProperty("Items")]
43 [DefaultEvent("SelectedIndexChanged")]
44 [Designer ("System.Windows.Forms.Design.ListBoxDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
45 [DefaultBindingProperty ("SelectedValue")]
46 [ClassInterface (ClassInterfaceType.AutoDispatch)]
48 public class ListBox : ListControl
50 public const int DefaultItemHeight = 13;
51 public const int NoMatches = -1;
53 internal enum ItemNavigation
65 Hashtable item_heights;
66 private int item_height = -1;
67 private int column_width = 0;
68 private int requested_height;
69 private DrawMode draw_mode = DrawMode.Normal;
70 private int horizontal_extent = 0;
71 private bool horizontal_scrollbar = false;
72 private bool integral_height = true;
73 private bool multicolumn = false;
74 private bool scroll_always_visible = false;
75 private SelectedIndexCollection selected_indices;
76 private SelectedObjectCollection selected_items;
77 private SelectionMode selection_mode = SelectionMode.One;
78 private bool sorted = false;
79 private bool use_tabstops = true;
80 private int column_width_internal = 120;
81 private ImplicitVScrollBar vscrollbar;
82 private ImplicitHScrollBar hscrollbar;
83 private int hbar_offset;
84 private bool suspend_layout;
85 private bool ctrl_pressed = false;
86 private bool shift_pressed = false;
87 private bool explicit_item_height = false;
88 private int top_index = 0;
89 private int last_visible_index = 0;
90 private Rectangle items_area;
91 private int focused_item = -1;
92 private ObjectCollection items;
93 private IntegerCollection custom_tab_offsets;
94 private Padding padding;
95 private bool use_custom_tab_offsets;
99 items = CreateItemCollection ();
100 selected_indices = new SelectedIndexCollection (this);
101 selected_items = new SelectedObjectCollection (this);
103 requested_height = bounds.Height;
104 InternalBorderStyle = BorderStyle.Fixed3D;
105 BackColor = ThemeEngine.Current.ColorWindow;
107 /* Vertical scrollbar */
108 vscrollbar = new ImplicitVScrollBar ();
109 vscrollbar.Minimum = 0;
110 vscrollbar.SmallChange = 1;
111 vscrollbar.LargeChange = 1;
112 vscrollbar.Maximum = 0;
113 vscrollbar.ValueChanged += new EventHandler (VerticalScrollEvent);
114 vscrollbar.Visible = false;
116 /* Horizontal scrollbar */
117 hscrollbar = new ImplicitHScrollBar ();
118 hscrollbar.Minimum = 0;
119 hscrollbar.SmallChange = 1;
120 hscrollbar.LargeChange = 1;
121 hscrollbar.Maximum = 0;
122 hscrollbar.Visible = false;
123 hscrollbar.ValueChanged += new EventHandler (HorizontalScrollEvent);
125 Controls.AddImplicit (vscrollbar);
126 Controls.AddImplicit (hscrollbar);
129 MouseDown += new MouseEventHandler (OnMouseDownLB);
130 MouseMove += new MouseEventHandler (OnMouseMoveLB);
131 MouseUp += new MouseEventHandler (OnMouseUpLB);
132 MouseWheel += new MouseEventHandler (OnMouseWheelLB);
133 KeyUp += new KeyEventHandler (OnKeyUpLB);
134 GotFocus += new EventHandler (OnGotFocus);
135 LostFocus += new EventHandler (OnLostFocus);
137 SetStyle (ControlStyles.UserPaint, false);
139 custom_tab_offsets = new IntegerCollection (this);
143 static object DrawItemEvent = new object ();
144 static object MeasureItemEvent = new object ();
145 static object SelectedIndexChangedEvent = new object ();
148 [EditorBrowsable (EditorBrowsableState.Never)]
149 public new event EventHandler BackgroundImageChanged {
150 add { base.BackgroundImageChanged += value; }
151 remove { base.BackgroundImageChanged -= value; }
155 [EditorBrowsable (EditorBrowsableState.Never)]
156 public new event EventHandler BackgroundImageLayoutChanged {
157 add { base.BackgroundImageLayoutChanged += value; }
158 remove { base.BackgroundImageLayoutChanged -= value; }
162 [EditorBrowsable (EditorBrowsableState.Always)]
163 public new event EventHandler Click {
164 add { base.Click += value; }
165 remove { base.Click -= value; }
168 public event DrawItemEventHandler DrawItem {
169 add { Events.AddHandler (DrawItemEvent, value); }
170 remove { Events.RemoveHandler (DrawItemEvent, value); }
173 public event MeasureItemEventHandler MeasureItem {
174 add { Events.AddHandler (MeasureItemEvent, value); }
175 remove { Events.RemoveHandler (MeasureItemEvent, value); }
179 [EditorBrowsable (EditorBrowsableState.Always)]
180 public new event MouseEventHandler MouseClick {
181 add { base.MouseClick += value; }
182 remove { base.MouseClick -= value; }
186 [EditorBrowsable (EditorBrowsableState.Never)]
187 public new event EventHandler PaddingChanged {
188 add { base.PaddingChanged += value; }
189 remove { base.PaddingChanged -= value; }
193 [EditorBrowsable (EditorBrowsableState.Never)]
194 public new event PaintEventHandler Paint {
195 add { base.Paint += value; }
196 remove { base.Paint -= value; }
199 public event EventHandler SelectedIndexChanged {
200 add { Events.AddHandler (SelectedIndexChangedEvent, value); }
201 remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); }
205 [EditorBrowsable (EditorBrowsableState.Advanced)]
206 public new event EventHandler TextChanged {
207 add { base.TextChanged += value; }
208 remove { base.TextChanged -= value; }
212 #region UIA Framework Events
214 // We are using Reflection to add/remove internal events.
215 // Class ListProvider uses the events.
217 //Event used to generate UIA Selection Pattern
218 static object UIASelectionModeChangedEvent = new object ();
220 internal event EventHandler UIASelectionModeChanged {
221 add { Events.AddHandler (UIASelectionModeChangedEvent, value); }
222 remove { Events.RemoveHandler (UIASelectionModeChangedEvent, value); }
225 internal void OnUIASelectionModeChangedEvent ()
227 EventHandler eh = (EventHandler) Events [UIASelectionModeChangedEvent];
229 eh (this, EventArgs.Empty);
232 static object UIAFocusedItemChangedEvent = new object ();
234 internal event EventHandler UIAFocusedItemChanged {
235 add { Events.AddHandler (UIAFocusedItemChangedEvent, value); }
236 remove { Events.RemoveHandler (UIAFocusedItemChangedEvent, value); }
239 internal void OnUIAFocusedItemChangedEvent ()
241 EventHandler eh = (EventHandler) Events [UIAFocusedItemChangedEvent];
243 eh (this, EventArgs.Empty);
245 #endregion UIA Framework Events
247 #region Public Properties
248 public override Color BackColor {
249 get { return base.BackColor; }
251 if (base.BackColor == value)
254 base.BackColor = value;
255 base.Refresh (); // Careful. Calling the base method is not the same that calling
256 } // the overriden one that refresh also all the items
260 [EditorBrowsable (EditorBrowsableState.Never)]
261 public override Image BackgroundImage {
262 get { return base.BackgroundImage; }
264 base.BackgroundImage = value;
270 [EditorBrowsable (EditorBrowsableState.Never)]
271 public override ImageLayout BackgroundImageLayout {
272 get { return base.BackgroundImageLayout; }
273 set { base.BackgroundImageLayout = value; }
276 [DefaultValue (BorderStyle.Fixed3D)]
278 public BorderStyle BorderStyle {
279 get { return InternalBorderStyle; }
281 InternalBorderStyle = value;
282 UpdateListBoxBounds ();
288 public int ColumnWidth {
289 get { return column_width; }
292 throw new ArgumentException ("A value less than zero is assigned to the property.");
294 column_width = value;
297 ColumnWidthInternal = 120;
299 ColumnWidthInternal = value;
305 protected override CreateParams CreateParams {
306 get { return base.CreateParams;}
310 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
311 public IntegerCollection CustomTabOffsets {
312 get { return custom_tab_offsets; }
315 protected override Size DefaultSize {
316 get { return new Size (120, 96); }
319 [RefreshProperties(RefreshProperties.Repaint)]
320 [DefaultValue (DrawMode.Normal)]
321 public virtual DrawMode DrawMode {
322 get { return draw_mode; }
324 if (!Enum.IsDefined (typeof (DrawMode), value))
325 throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for DrawMode", value));
327 if (value == DrawMode.OwnerDrawVariable && multicolumn == true)
328 throw new ArgumentException ("Cannot have variable height and multicolumn");
330 if (draw_mode == value)
335 if (draw_mode == DrawMode.OwnerDrawVariable)
336 item_heights = new Hashtable ();
341 Parent.PerformLayout (this, "DrawMode");
346 public override Font Font {
347 get { return base.Font; }
348 set { base.Font = value; }
351 public override Color ForeColor {
352 get { return base.ForeColor; }
354 if (base.ForeColor == value)
357 base.ForeColor = value;
364 public int HorizontalExtent {
365 get { return horizontal_extent; }
367 if (horizontal_extent == value)
370 horizontal_extent = value;
375 [DefaultValue (false)]
377 public bool HorizontalScrollbar {
378 get { return horizontal_scrollbar; }
380 if (horizontal_scrollbar == value)
383 horizontal_scrollbar = value;
389 [DefaultValue (true)]
391 [RefreshProperties(RefreshProperties.Repaint)]
392 public bool IntegralHeight {
393 get { return integral_height; }
395 if (integral_height == value)
398 integral_height = value;
399 UpdateListBoxBounds ();
405 [RefreshProperties(RefreshProperties.Repaint)]
406 public virtual int ItemHeight {
408 if (item_height == -1) {
409 SizeF sz = TextRenderer.MeasureString ("The quick brown Fox", Font);
410 item_height = (int) sz.Height;
416 throw new ArgumentOutOfRangeException ("The ItemHeight property was set beyond 255 pixels");
418 explicit_item_height = true;
419 if (item_height == value)
424 UpdateListBoxBounds ();
429 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
431 [Editor ("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
432 [MergableProperty (false)]
433 public ObjectCollection Items {
434 get { return items; }
437 [DefaultValue (false)]
438 public bool MultiColumn {
439 get { return multicolumn; }
441 if (multicolumn == value)
444 if (value == true && DrawMode == DrawMode.OwnerDrawVariable)
445 throw new ArgumentException ("A multicolumn ListBox cannot have a variable-sized height.");
454 [EditorBrowsable (EditorBrowsableState.Never)]
455 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
456 public new Padding Padding {
457 get { return padding; }
458 set { padding = value; }
462 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
463 [EditorBrowsable (EditorBrowsableState.Advanced)]
464 public int PreferredHeight {
467 if (draw_mode == DrawMode.Normal)
468 itemsHeight = FontHeight * items.Count;
469 else if (draw_mode == DrawMode.OwnerDrawFixed)
470 itemsHeight = ItemHeight * items.Count;
471 else if (draw_mode == DrawMode.OwnerDrawVariable) {
472 for (int i = 0; i < items.Count; i++)
473 itemsHeight += (int) item_heights [Items [i]];
480 public override RightToLeft RightToLeft {
481 get { return base.RightToLeft; }
483 base.RightToLeft = value;
484 if (base.RightToLeft == RightToLeft.Yes)
485 StringFormat.Alignment = StringAlignment.Far;
487 StringFormat.Alignment = StringAlignment.Near;
492 // Only affects the Vertical ScrollBar
493 [DefaultValue (false)]
495 public bool ScrollAlwaysVisible {
496 get { return scroll_always_visible; }
498 if (scroll_always_visible == value)
501 scroll_always_visible = value;
508 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
509 public override int SelectedIndex {
511 if (selected_indices == null)
514 return selected_indices.Count > 0 ? selected_indices [0] : -1;
517 if (value < -1 || value >= Items.Count)
518 throw new ArgumentOutOfRangeException ("Index of out range");
520 if (SelectionMode == SelectionMode.None)
521 throw new ArgumentException ("cannot call this method if SelectionMode is SelectionMode.None");
524 selected_indices.Clear ();
526 selected_indices.Add (value);
531 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
532 public SelectedIndexCollection SelectedIndices {
533 get { return selected_indices; }
538 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
539 public object SelectedItem {
541 if (SelectedItems.Count > 0)
542 return SelectedItems[0];
547 if (value != null && !Items.Contains (value))
548 return; // FIXME: this is probably an exception
550 SelectedIndex = value == null ? - 1 : Items.IndexOf (value);
555 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
556 public SelectedObjectCollection SelectedItems {
557 get {return selected_items;}
560 [DefaultValue (SelectionMode.One)]
561 public virtual SelectionMode SelectionMode {
562 get { return selection_mode; }
564 if (!Enum.IsDefined (typeof (SelectionMode), value))
565 throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for SelectionMode", value));
567 if (selection_mode == value)
570 selection_mode = value;
572 switch (selection_mode) {
573 case SelectionMode.None:
574 SelectedIndices.Clear ();
577 case SelectionMode.One:
578 // FIXME: Probably this can be improved
579 ArrayList old_selection = (ArrayList) SelectedIndices.List.Clone ();
580 for (int i = 1; i < old_selection.Count; i++)
581 SelectedIndices.Remove ((int)old_selection [i]);
588 // UIA Framework: Generates SelectionModeChanged event.
589 OnUIASelectionModeChangedEvent ();
593 [DefaultValue (false)]
595 get { return sorted; }
608 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
609 [EditorBrowsable (EditorBrowsableState.Advanced)]
610 public override string Text {
612 if (SelectionMode != SelectionMode.None && SelectedIndex != -1)
613 return GetItemText (SelectedItem);
621 if (SelectionMode == SelectionMode.None)
626 index = FindStringExact (value);
631 SelectedIndex = index;
636 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
637 public int TopIndex {
638 get { return top_index; }
640 if (value == top_index)
643 if (value < 0 || value >= Items.Count)
646 int page_size = (items_area.Height / ItemHeight);
648 if (Items.Count < page_size)
650 else if (!multicolumn)
651 top_index = Math.Min (value, Items.Count - page_size);
661 [DefaultValue (false)]
662 public bool UseCustomTabOffsets {
663 get { return use_custom_tab_offsets; }
665 if (use_custom_tab_offsets != value) {
666 use_custom_tab_offsets = value;
667 CalculateTabStops ();
672 [DefaultValue (true)]
673 public bool UseTabStops {
674 get { return use_tabstops; }
676 if (use_tabstops == value)
679 use_tabstops = value;
680 CalculateTabStops ();
684 protected override bool AllowSelection {
686 return SelectionMode != SelectionMode.None;
689 #endregion Public Properties
691 #region Private Properties
693 private int ColumnWidthInternal {
694 get { return column_width_internal; }
695 set { column_width_internal = value; }
698 private int row_count = 1;
699 private int RowCount {
701 return MultiColumn ? row_count : Items.Count;
705 #endregion Private Properties
707 #region UIA Framework Properties
709 internal ScrollBar UIAHScrollBar {
710 get { return hscrollbar; }
713 internal ScrollBar UIAVScrollBar {
714 get { return vscrollbar; }
717 #endregion UIA Framework Properties
719 #region Public Methods
720 [Obsolete ("this method has been deprecated")]
721 protected virtual void AddItemsCore (object[] value)
723 Items.AddRange (value);
726 public void BeginUpdate ()
728 suspend_layout = true;
731 public void ClearSelected ()
733 selected_indices.Clear ();
736 protected virtual ObjectCollection CreateItemCollection ()
738 return new ObjectCollection (this);
741 public void EndUpdate ()
743 suspend_layout = false;
748 public int FindString (String s)
750 return FindString (s, -1);
753 public int FindString (string s, int startIndex)
755 if (Items.Count == 0)
756 return -1; // No exception throwing if empty
758 if (startIndex < -1 || startIndex >= Items.Count)
759 throw new ArgumentOutOfRangeException ("Index of out range");
761 startIndex = (startIndex == Items.Count - 1) ? 0 : startIndex + 1;
765 string text = GetItemText (Items [i]);
766 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (text, s,
767 CompareOptions.IgnoreCase))
770 i = (i == Items.Count - 1) ? 0 : i + 1;
778 public int FindStringExact (string s)
780 return FindStringExact (s, -1);
783 public int FindStringExact (string s, int startIndex)
785 if (Items.Count == 0)
786 return -1; // No exception throwing if empty
788 if (startIndex < -1 || startIndex >= Items.Count)
789 throw new ArgumentOutOfRangeException ("Index of out range");
791 startIndex = (startIndex + 1 == Items.Count) ? 0 : startIndex + 1;
795 if (String.Compare (GetItemText (Items[i]), s, true) == 0)
798 i = (i + 1 == Items.Count) ? 0 : i + 1;
806 public int GetItemHeight (int index)
808 if (index < 0 || index >= Items.Count)
809 throw new ArgumentOutOfRangeException ("Index of out range");
811 if (DrawMode == DrawMode.OwnerDrawVariable && IsHandleCreated == true) {
813 object o = Items [index];
814 if (item_heights.Contains (o))
815 return (int) item_heights [o];
817 MeasureItemEventArgs args = new MeasureItemEventArgs (DeviceContext, index, ItemHeight);
818 OnMeasureItem (args);
819 item_heights [o] = args.ItemHeight;
820 return args.ItemHeight;
826 public Rectangle GetItemRectangle (int index)
828 if (index < 0 || index >= Items.Count)
829 throw new ArgumentOutOfRangeException ("GetItemRectangle index out of range.");
831 Rectangle rect = new Rectangle ();
834 int col = index / RowCount;
836 if (y < 0) // We convert it to valid positive value
837 y += RowCount * (top_index / RowCount);
838 rect.Y = (y % RowCount) * ItemHeight;
839 rect.X = (col - (top_index / RowCount)) * ColumnWidthInternal;
840 rect.Height = ItemHeight;
841 rect.Width = ColumnWidthInternal;
844 rect.Height = GetItemHeight (index);
845 rect.Width = items_area.Width;
847 if (DrawMode == DrawMode.OwnerDrawVariable) {
849 if (index >= top_index) {
850 for (int i = top_index; i < index; i++) {
851 rect.Y += GetItemHeight (i);
854 for (int i = index; i < top_index; i++) {
855 rect.Y -= GetItemHeight (i);
859 rect.Y = ItemHeight * (index - top_index);
863 if (this is CheckedListBox)
869 [EditorBrowsable (EditorBrowsableState.Advanced)]
870 protected override Rectangle GetScaledBounds (Rectangle bounds, SizeF factor, BoundsSpecified specified)
872 bounds.Height = requested_height;
874 return base.GetScaledBounds (bounds, factor, specified);
877 public bool GetSelected (int index)
879 if (index < 0 || index >= Items.Count)
880 throw new ArgumentOutOfRangeException ("Index of out range");
882 return SelectedIndices.Contains (index);
885 public int IndexFromPoint (Point p)
887 return IndexFromPoint (p.X, p.Y);
890 // Only returns visible points
891 public int IndexFromPoint (int x, int y)
894 if (Items.Count == 0) {
898 for (int i = top_index; i <= last_visible_index; i++) {
899 if (GetItemRectangle (i).Contains (x,y) == true)
906 protected override void OnChangeUICues (UICuesEventArgs e)
908 base.OnChangeUICues (e);
911 protected override void OnDataSourceChanged (EventArgs e)
913 base.OnDataSourceChanged (e);
916 if (DataSource == null || DataManager == null) {
919 SelectedIndex = DataManager.Position;
923 protected override void OnDisplayMemberChanged (EventArgs e)
925 base.OnDisplayMemberChanged (e);
927 if (DataManager == null || !IsHandleCreated)
934 protected virtual void OnDrawItem (DrawItemEventArgs e)
937 case DrawMode.OwnerDrawFixed:
938 case DrawMode.OwnerDrawVariable:
939 DrawItemEventHandler eh = (DrawItemEventHandler)(Events [DrawItemEvent]);
946 ThemeEngine.Current.DrawListBoxItem (this, e);
951 protected override void OnFontChanged (EventArgs e)
953 base.OnFontChanged (e);
956 StringFormat.SetTabStops (0, new float [] {(float)(Font.Height * 3.7)});
958 if (explicit_item_height) {
961 SizeF sz = TextRenderer.MeasureString ("The quick brown Fox", Font);
962 item_height = (int) sz.Height;
964 UpdateListBoxBounds ();
969 protected override void OnHandleCreated (EventArgs e)
971 base.OnHandleCreated (e);
974 UpdateListBoxBounds ();
977 EnsureVisible (focused_item);
980 protected override void OnHandleDestroyed (EventArgs e)
982 base.OnHandleDestroyed (e);
985 protected virtual void OnMeasureItem (MeasureItemEventArgs e)
987 if (draw_mode != DrawMode.OwnerDrawVariable)
990 MeasureItemEventHandler eh = (MeasureItemEventHandler)(Events [MeasureItemEvent]);
995 protected override void OnParentChanged (EventArgs e)
997 base.OnParentChanged (e);
1000 protected override void OnResize (EventArgs e)
1003 if (canvas_size.IsEmpty || MultiColumn)
1009 protected override void OnSelectedIndexChanged (EventArgs e)
1011 base.OnSelectedIndexChanged (e);
1013 EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]);
1018 protected override void OnSelectedValueChanged (EventArgs e)
1020 base.OnSelectedValueChanged (e);
1023 public override void Refresh ()
1025 if (draw_mode == DrawMode.OwnerDrawVariable)
1026 item_heights.Clear ();
1031 protected override void RefreshItem (int index)
1033 if (index < 0 || index >= Items.Count)
1034 throw new ArgumentOutOfRangeException ("Index of out range");
1036 if (draw_mode == DrawMode.OwnerDrawVariable)
1037 item_heights.Remove (Items [index]);
1040 protected override void RefreshItems ()
1042 for (int i = 0; i < Items.Count; i++) {
1050 public override void ResetBackColor ()
1052 base.ResetBackColor ();
1055 public override void ResetForeColor ()
1057 base.ResetForeColor ();
1060 protected override void ScaleControl (SizeF factor, BoundsSpecified specified)
1062 base.ScaleControl (factor, specified);
1065 private int SnapHeightToIntegral (int height)
1069 switch (border_style) {
1070 case BorderStyle.Fixed3D:
1071 border = ThemeEngine.Current.Border3DSize.Height;
1073 case BorderStyle.FixedSingle:
1074 border = ThemeEngine.Current.BorderSize.Height;
1076 case BorderStyle.None:
1082 height -= (2 * border);
1083 height -= height % ItemHeight;
1084 height += (2 * border);
1089 protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
1091 if ((specified & BoundsSpecified.Height) == BoundsSpecified.Height)
1092 requested_height = height;
1094 if (IntegralHeight && IsHandleCreated)
1095 height = SnapHeightToIntegral (height);
1097 base.SetBoundsCore (x, y, width, height, specified);
1098 UpdateScrollBars ();
1099 last_visible_index = LastVisibleItem ();
1102 protected override void SetItemCore (int index, object value)
1104 if (index < 0 || index >= Items.Count)
1107 Items[index] = value;
1110 protected override void SetItemsCore (IList value)
1115 Items.AddItems (value);
1121 public void SetSelected (int index, bool value)
1123 if (index < 0 || index >= Items.Count)
1124 throw new ArgumentOutOfRangeException ("Index of out range");
1126 if (SelectionMode == SelectionMode.None)
1127 throw new InvalidOperationException ();
1130 SelectedIndices.Add (index);
1132 SelectedIndices.Remove (index);
1135 protected virtual void Sort ()
1141 // Sometimes we could need to Sort, and request a Refresh
1142 // in a different place, to not have the painting done twice
1144 void Sort (bool paint)
1146 if (Items.Count == 0)
1155 public override string ToString ()
1157 return base.ToString ();
1160 protected virtual void WmReflectCommand (ref Message m)
1164 protected override void WndProc (ref Message m)
1166 if ((Msg)m.Msg == Msg.WM_KEYDOWN) {
1167 if (ProcessKeyMessage (ref m))
1168 m.Result = IntPtr.Zero;
1170 HandleKeyDown ((Keys)m.WParam.ToInt32 ());
1177 base.WndProc (ref m);
1180 #endregion Public Methods
1182 #region Private Methods
1184 private void CalculateTabStops ()
1187 if (use_custom_tab_offsets) {
1188 float[] f = new float[custom_tab_offsets.Count];
1189 custom_tab_offsets.CopyTo (f, 0);
1190 StringFormat.SetTabStops (0, f);
1193 StringFormat.SetTabStops (0, new float[] { (float)(Font.Height * 3.7) });
1195 StringFormat.SetTabStops (0, new float[0]);
1200 private Size canvas_size;
1202 private void LayoutListBox ()
1204 if (!IsHandleCreated || suspend_layout)
1208 LayoutMultiColumn ();
1210 LayoutSingleColumn ();
1212 last_visible_index = LastVisibleItem ();
1213 UpdateScrollBars ();
1216 private void LayoutSingleColumn ()
1221 case DrawMode.OwnerDrawVariable:
1223 width = HorizontalExtent;
1224 for (int i = 0; i < Items.Count; i++) {
1225 height += GetItemHeight (i);
1229 case DrawMode.OwnerDrawFixed:
1230 height = Items.Count * ItemHeight;
1231 width = HorizontalExtent;
1234 case DrawMode.Normal:
1236 height = Items.Count * ItemHeight;
1238 for (int i = 0; i < Items.Count; i++) {
1239 SizeF sz = TextRenderer.MeasureString (GetItemText (Items[i]), Font);
1240 int t = (int)sz.Width;
1242 if (this is CheckedListBox)
1251 canvas_size = new Size (width, height);
1254 private void LayoutMultiColumn ()
1256 int usable_height = ClientRectangle.Height - (ScrollAlwaysVisible ? hscrollbar.Height : 0);
1257 row_count = Math.Max (1, usable_height / ItemHeight);
1259 int cols = (int) Math.Ceiling ((float)Items.Count / (float) row_count);
1260 Size sz = new Size (cols * ColumnWidthInternal, row_count * ItemHeight);
1261 if (!ScrollAlwaysVisible && sz.Width > ClientRectangle.Width && row_count > 1) {
1262 usable_height = ClientRectangle.Height - hscrollbar.Height;
1263 row_count = Math.Max (1, usable_height / ItemHeight);
1264 cols = (int) Math.Ceiling ((float)Items.Count / (float) row_count);
1265 sz = new Size (cols * ColumnWidthInternal, row_count * ItemHeight);
1270 internal void Draw (Rectangle clip, Graphics dc)
1272 Theme theme = ThemeEngine.Current;
1274 if (hscrollbar.Visible && vscrollbar.Visible) {
1275 // Paint the dead space in the bottom right corner
1276 Rectangle rect = new Rectangle (hscrollbar.Right, vscrollbar.Bottom, vscrollbar.Width, hscrollbar.Height);
1277 if (rect.IntersectsWith (clip))
1278 dc.FillRectangle (theme.ResPool.GetSolidBrush (theme.ColorControl), rect);
1281 dc.FillRectangle (theme.ResPool.GetSolidBrush (BackColor), items_area);
1283 if (Items.Count == 0)
1286 for (int i = top_index; i <= last_visible_index; i++) {
1287 Rectangle rect = GetItemDisplayRectangle (i, top_index);
1289 if (!clip.IntersectsWith (rect))
1292 DrawItemState state = DrawItemState.None;
1294 if (SelectedIndices.Contains (i))
1295 state |= DrawItemState.Selected;
1297 if (has_focus && FocusedItem == i)
1298 state |= DrawItemState.Focus;
1300 if (MultiColumn == false && hscrollbar != null && hscrollbar.Visible) {
1301 rect.X -= hscrollbar.Value;
1302 rect.Width += hscrollbar.Value;
1305 Color fore_color = (state & DrawItemState.Selected) != 0 ? ThemeEngine.Current.ColorHighlightText : ForeColor;
1306 OnDrawItem (new DrawItemEventArgs (dc, Font, rect, i, state, fore_color, BackColor));
1310 // Converts a GetItemRectangle to a one that we can display
1311 internal Rectangle GetItemDisplayRectangle (int index, int first_displayble)
1313 Rectangle item_rect;
1314 Rectangle first_item_rect = GetItemRectangle (first_displayble);
1315 item_rect = GetItemRectangle (index);
1316 item_rect.X -= first_item_rect.X;
1317 item_rect.Y -= first_item_rect.Y;
1319 // Subtract the checkboxes from the width
1320 if (this is CheckedListBox)
1321 item_rect.Width -= 14;
1327 private void HorizontalScrollEvent (object sender, EventArgs e)
1330 int top_item = top_index;
1331 int last_item = last_visible_index;
1333 top_index = RowCount * hscrollbar.Value;
1334 last_visible_index = LastVisibleItem ();
1336 if (top_item != top_index || last_item != last_visible_index)
1337 Invalidate (items_area);
1340 int old_offset = hbar_offset;
1341 hbar_offset = hscrollbar.Value;
1343 if (hbar_offset < 0)
1346 if (IsHandleCreated) {
1347 XplatUI.ScrollWindow (Handle, items_area, old_offset - hbar_offset, 0, false);
1349 // Invalidate the previous selection border, to keep it properly updated.
1350 Rectangle selection_border_area = new Rectangle (items_area.Width - (hbar_offset - old_offset) - 3, 0,
1351 3, items_area.Height);
1352 Invalidate (selection_border_area);
1357 // Only returns visible points. The diference of with IndexFromPoint is that the rectangle
1358 // has screen coordinates
1359 private int IndexAtClientPoint (int x, int y)
1361 if (Items.Count == 0)
1366 else if (x > ClientRectangle.Right)
1367 x = ClientRectangle.Right;
1371 else if (y > ClientRectangle.Bottom)
1372 y = ClientRectangle.Bottom;
1374 for (int i = top_index; i <= last_visible_index; i++)
1375 if (GetItemDisplayRectangle (i, top_index).Contains (x, y))
1381 internal override bool IsInputCharInternal (char charCode)
1386 private int LastVisibleItem ()
1388 Rectangle item_rect;
1389 int top_y = items_area.Y + items_area.Height;
1392 if (top_index >= Items.Count)
1395 for (i = top_index; i < Items.Count; i++) {
1396 item_rect = GetItemDisplayRectangle (i, top_index);
1398 if (item_rect.X > items_area.Width)
1401 if (item_rect.Y + item_rect.Height > top_y)
1408 private void UpdateTopItem ()
1411 int col = top_index / RowCount;
1413 if (col > hscrollbar.Maximum)
1414 hscrollbar.Value = hscrollbar.Maximum;
1416 hscrollbar.Value = col;
1418 if (top_index > vscrollbar.Maximum)
1419 vscrollbar.Value = vscrollbar.Maximum;
1421 vscrollbar.Value = top_index;
1422 Scroll (vscrollbar, vscrollbar.Value - top_index);
1426 // Navigates to the indicated item and returns the new item
1427 private int NavigateItemVisually (ItemNavigation navigation)
1429 int page_size, columns, selected_index = -1;
1432 columns = items_area.Width / ColumnWidthInternal;
1433 page_size = columns * RowCount;
1434 if (page_size == 0) {
1435 page_size = RowCount;
1438 page_size = items_area.Height / ItemHeight;
1441 switch (navigation) {
1443 case ItemNavigation.PreviousColumn: {
1444 if (SelectedIndex - RowCount < 0) {
1448 if (SelectedIndex - RowCount < top_index) {
1449 top_index = SelectedIndex - RowCount;
1453 selected_index = SelectedIndex - RowCount;
1457 case ItemNavigation.NextColumn: {
1458 if (SelectedIndex + RowCount >= Items.Count) {
1462 if (SelectedIndex + RowCount > last_visible_index) {
1463 top_index = SelectedIndex;
1467 selected_index = SelectedIndex + RowCount;
1471 case ItemNavigation.First: {
1478 case ItemNavigation.Last: {
1480 int rows = items_area.Height / ItemHeight;
1483 selected_index = Items.Count - 1;
1486 if (Items.Count < rows) {
1488 selected_index = Items.Count - 1;
1491 top_index = Items.Count - rows;
1492 selected_index = Items.Count - 1;
1498 case ItemNavigation.Next: {
1499 if (FocusedItem == Items.Count - 1)
1503 selected_index = FocusedItem + 1;
1508 ArrayList heights = new ArrayList ();
1509 if (draw_mode == DrawMode.OwnerDrawVariable) {
1510 for (int i = top_index; i <= FocusedItem + 1; i++) {
1511 int h = GetItemHeight (i);
1516 bottom = ((FocusedItem + 1) - top_index + 1) * ItemHeight;
1519 if (bottom >= items_area.Height) {
1520 int overhang = bottom - items_area.Height;
1523 if (draw_mode == DrawMode.OwnerDrawVariable)
1524 while (overhang > 0)
1525 overhang -= (int) heights [offset];
1527 offset = (int) Math.Ceiling ((float)overhang / (float) ItemHeight);
1528 top_index += offset;
1531 selected_index = FocusedItem + 1;
1535 case ItemNavigation.Previous: {
1536 if (FocusedItem > 0) {
1537 if (FocusedItem - 1 < top_index) {
1541 selected_index = FocusedItem - 1;
1546 case ItemNavigation.NextPage: {
1547 if (Items.Count < page_size) {
1548 NavigateItemVisually (ItemNavigation.Last);
1552 if (FocusedItem + page_size - 1 >= Items.Count) {
1553 top_index = Items.Count - page_size;
1555 selected_index = Items.Count - 1;
1558 if (FocusedItem + page_size - 1 > last_visible_index) {
1559 top_index = FocusedItem;
1563 selected_index = FocusedItem + page_size - 1;
1569 case ItemNavigation.PreviousPage: {
1571 int rows = items_area.Height / ItemHeight;
1572 if (FocusedItem - (rows - 1) <= 0) {
1578 if (SelectedIndex - (rows - 1) < top_index) {
1579 top_index = FocusedItem - (rows - 1);
1583 selected_index = FocusedItem - (rows - 1);
1592 return selected_index;
1596 private void OnGotFocus (object sender, EventArgs e)
1598 if (Items.Count == 0)
1601 if (FocusedItem == -1)
1604 InvalidateItem (FocusedItem);
1607 private void OnLostFocus (object sender, EventArgs e)
1609 if (FocusedItem != -1)
1610 InvalidateItem (FocusedItem);
1613 private bool KeySearch (Keys key)
1615 char c = (char) key;
1616 if (!Char.IsLetterOrDigit (c))
1619 int idx = FindString (c.ToString (), SelectedIndex);
1620 if (idx != ListBox.NoMatches)
1621 SelectedIndex = idx;
1626 internal void HandleKeyDown (Keys key)
1630 if (Items.Count == 0)
1633 if (KeySearch (key))
1638 case Keys.ControlKey:
1639 ctrl_pressed = true;
1643 shift_pressed = true;
1647 new_item = NavigateItemVisually (ItemNavigation.First);
1651 new_item = NavigateItemVisually (ItemNavigation.Last);
1655 new_item = NavigateItemVisually (ItemNavigation.Previous);
1659 new_item = NavigateItemVisually (ItemNavigation.Next);
1663 new_item = NavigateItemVisually (ItemNavigation.PreviousPage);
1667 new_item = NavigateItemVisually (ItemNavigation.NextPage);
1671 if (multicolumn == true) {
1672 new_item = NavigateItemVisually (ItemNavigation.NextColumn);
1677 if (multicolumn == true) {
1678 new_item = NavigateItemVisually (ItemNavigation.PreviousColumn);
1683 if (selection_mode == SelectionMode.MultiSimple) {
1684 SelectedItemFromNavigation (FocusedItem);
1693 if (new_item != -1) {
1694 FocusedItem = new_item;
1696 if (selection_mode != SelectionMode.MultiSimple)
1697 SelectedItemFromNavigation (new_item);
1701 private void OnKeyUpLB (object sender, KeyEventArgs e)
1703 switch (e.KeyCode) {
1704 case Keys.ControlKey:
1705 ctrl_pressed = false;
1708 shift_pressed = false;
1715 internal void InvalidateItem (int index)
1717 if (!IsHandleCreated)
1719 Rectangle bounds = GetItemDisplayRectangle (index, top_index);
1720 if (ClientRectangle.IntersectsWith (bounds))
1721 Invalidate (bounds);
1724 internal virtual void OnItemClick (int index)
1726 OnSelectedIndexChanged (EventArgs.Empty);
1727 OnSelectedValueChanged (EventArgs.Empty);
1731 int[] prev_selection;
1732 bool button_pressed = false;
1733 Point button_pressed_loc = new Point (-1, -1);
1735 private void SelectExtended (int index)
1739 ArrayList new_selection = new ArrayList ();
1740 int start = anchor < index ? anchor : index;
1741 int end = anchor > index ? anchor : index;
1742 for (int i = start; i <= end; i++)
1743 new_selection.Add (i);
1746 foreach (int i in prev_selection)
1747 if (!new_selection.Contains (i))
1748 new_selection.Add (i);
1750 // Need to make a copy since we can't enumerate and modify the collection
1752 ArrayList sel_indices = (ArrayList) selected_indices.List.Clone ();
1753 foreach (int i in sel_indices)
1754 if (!new_selection.Contains (i))
1755 selected_indices.Remove (i);
1757 foreach (int i in new_selection)
1758 if (!sel_indices.Contains (i))
1759 selected_indices.AddCore (i);
1763 private void OnMouseDownLB (object sender, MouseEventArgs e)
1765 // Only do stuff with the left mouse button
1766 if ((e.Button & MouseButtons.Left) == 0)
1769 int index = IndexAtClientPoint (e.X, e.Y);
1773 switch (SelectionMode) {
1774 case SelectionMode.One:
1775 SelectedIndices.AddCore (index); // Unselects previous one
1778 case SelectionMode.MultiSimple:
1779 if (SelectedIndices.Contains (index))
1780 SelectedIndices.RemoveCore (index);
1782 SelectedIndices.AddCore (index);
1785 case SelectionMode.MultiExtended:
1786 shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
1787 ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
1789 if (shift_pressed) {
1790 SelectedIndices.ClearCore ();
1791 SelectExtended (index);
1798 prev_selection = new int [SelectedIndices.Count];
1799 SelectedIndices.CopyTo (prev_selection, 0);
1801 if (SelectedIndices.Contains (index))
1802 SelectedIndices.RemoveCore (index);
1804 SelectedIndices.AddCore (index);
1809 SelectedIndices.ClearCore ();
1810 SelectedIndices.AddCore (index);
1813 case SelectionMode.None:
1819 button_pressed = true;
1820 button_pressed_loc = new Point (e.X, e.Y);
1821 FocusedItem = index;
1824 private void OnMouseMoveLB (object sender, MouseEventArgs e)
1826 // Don't take into account MouseMove events generated with MouseDown
1827 if (!button_pressed || button_pressed_loc == new Point (e.X, e.Y))
1830 int index = IndexAtClientPoint (e.X, e.Y);
1834 switch (SelectionMode) {
1835 case SelectionMode.One:
1836 SelectedIndices.AddCore (index); // Unselects previous one
1839 case SelectionMode.MultiSimple:
1842 case SelectionMode.MultiExtended:
1843 SelectExtended (index);
1846 case SelectionMode.None:
1852 FocusedItem = index;
1855 internal override void OnDragDropEnd (DragDropEffects effects)
1857 // In the case of a DnD operation (started on MouseDown)
1858 // there will be no MouseUp event, so we need to reset
1860 button_pressed = false;
1863 private void OnMouseUpLB (object sender, MouseEventArgs e)
1865 // Only do stuff with the left mouse button
1866 if ((e.Button & MouseButtons.Left) == 0)
1870 OnDoubleClick (EventArgs.Empty);
1871 OnMouseDoubleClick (e);
1873 else if (e.Clicks == 1) {
1874 OnClick (EventArgs.Empty);
1878 if (!button_pressed)
1881 int index = IndexAtClientPoint (e.X, e.Y);
1882 OnItemClick (index);
1883 button_pressed = ctrl_pressed = shift_pressed = false;
1886 private void Scroll (ScrollBar scrollbar, int delta)
1888 if (delta == 0 || !scrollbar.Visible || !scrollbar.Enabled)
1892 if (scrollbar == hscrollbar)
1893 max = hscrollbar.Maximum - (items_area.Width / ColumnWidthInternal) + 1;
1895 max = vscrollbar.Maximum - (items_area.Height / ItemHeight) + 1;
1897 int val = scrollbar.Value + delta;
1900 else if (val < scrollbar.Minimum)
1901 val = scrollbar.Minimum;
1902 scrollbar.Value = val;
1905 private void OnMouseWheelLB (object sender, MouseEventArgs me)
1907 if (Items.Count == 0)
1910 int lines = me.Delta / 120;
1913 Scroll (hscrollbar, -SystemInformation.MouseWheelScrollLines * lines);
1915 Scroll (vscrollbar, -lines);
1918 internal override void OnPaintInternal (PaintEventArgs pevent)
1923 Draw (pevent.ClipRectangle, pevent.Graphics);
1926 internal void RepositionScrollBars ()
1928 if (vscrollbar.is_visible) {
1929 vscrollbar.Size = new Size (vscrollbar.Width, items_area.Height);
1930 vscrollbar.Location = new Point (items_area.Width, 0);
1933 if (hscrollbar.is_visible) {
1934 hscrollbar.Size = new Size (items_area.Width, hscrollbar.Height);
1935 hscrollbar.Location = new Point (0, items_area.Height);
1939 // An item navigation operation (mouse or keyboard) has caused to select a new item
1940 internal void SelectedItemFromNavigation (int index)
1942 switch (SelectionMode) {
1943 case SelectionMode.None:
1944 // .Net doesn't select the item, only ensures that it's visible
1945 // and fires the selection related events
1946 EnsureVisible (index);
1947 OnSelectedIndexChanged (EventArgs.Empty);
1948 OnSelectedValueChanged (EventArgs.Empty);
1950 case SelectionMode.One: {
1951 SelectedIndex = index;
1954 case SelectionMode.MultiSimple: {
1955 if (SelectedIndex == -1) {
1956 SelectedIndex = index;
1959 if (SelectedIndices.Contains (index))
1960 SelectedIndices.Remove (index);
1962 SelectedIndices.AddCore (index);
1964 OnSelectedIndexChanged (EventArgs.Empty);
1965 OnSelectedValueChanged (EventArgs.Empty);
1971 case SelectionMode.MultiExtended: {
1972 if (SelectedIndex == -1) {
1973 SelectedIndex = index;
1976 if (ctrl_pressed == false && shift_pressed == false) {
1977 SelectedIndices.Clear ();
1980 if (shift_pressed == true) {
1981 ShiftSelection (index);
1982 } else { // ctrl_pressed or single item
1983 SelectedIndices.AddCore (index);
1987 OnSelectedIndexChanged (EventArgs.Empty);
1988 OnSelectedValueChanged (EventArgs.Empty);
1998 private void ShiftSelection (int index)
2000 int shorter_item = -1, dist = Items.Count + 1, cur_dist;
2002 foreach (int idx in selected_indices) {
2004 cur_dist = idx - index;
2006 cur_dist = index - idx;
2009 if (cur_dist < dist) {
2015 if (shorter_item != -1) {
2018 if (shorter_item > index) {
2022 start = shorter_item;
2026 selected_indices.Clear ();
2027 for (int idx = start; idx <= end; idx++) {
2028 selected_indices.AddCore (idx);
2033 internal int FocusedItem {
2034 get { return focused_item; }
2036 if (focused_item == value)
2039 int prev = focused_item;
2041 focused_item = value;
2043 if (has_focus == false)
2047 InvalidateItem (prev);
2050 InvalidateItem (value);
2052 // UIA Framework: Generates FocusedItemChanged event.
2053 OnUIAFocusedItemChangedEvent ();
2057 StringFormat string_format;
2058 internal StringFormat StringFormat {
2060 if (string_format == null) {
2061 string_format = new StringFormat ();
2062 string_format.FormatFlags = StringFormatFlags.NoWrap;
2064 if (RightToLeft == RightToLeft.Yes)
2065 string_format.Alignment = StringAlignment.Far;
2067 string_format.Alignment = StringAlignment.Near;
2068 CalculateTabStops ();
2070 return string_format;
2074 internal virtual void CollectionChanged ()
2079 if (Items.Count == 0) {
2080 selected_indices.List.Clear ();
2084 if (Items.Count <= focused_item)
2085 focused_item = Items.Count - 1;
2087 if (!IsHandleCreated || suspend_layout)
2095 void EnsureVisible (int index)
2097 if (!IsHandleCreated || index == -1)
2100 if (index < top_index) {
2104 } else if (!multicolumn) {
2105 int rows = items_area.Height / ItemHeight;
2106 if (index >= (top_index + rows))
2107 top_index = index - rows + 1;
2111 int rows = Math.Max (1, items_area.Height / ItemHeight);
2112 int cols = Math.Max (1, items_area.Width / ColumnWidthInternal);
2114 if (index >= (top_index + (rows * cols))) {
2115 int incolumn = index / rows;
2116 top_index = (incolumn - (cols - 1)) * rows;
2124 private void UpdateListBoxBounds ()
2126 if (IsHandleCreated)
2127 SetBoundsInternal (bounds.X, bounds.Y, bounds.Width, IntegralHeight ? SnapHeightToIntegral (requested_height) : requested_height, BoundsSpecified.None);
2130 private void UpdateScrollBars ()
2132 items_area = ClientRectangle;
2133 if (UpdateHorizontalScrollBar ()) {
2134 items_area.Height -= hscrollbar.Height;
2135 if (UpdateVerticalScrollBar ()) {
2136 items_area.Width -= vscrollbar.Width;
2137 UpdateHorizontalScrollBar ();
2139 } else if (UpdateVerticalScrollBar ()) {
2140 items_area.Width -= vscrollbar.Width;
2141 if (UpdateHorizontalScrollBar ()) {
2142 items_area.Height -= hscrollbar.Height;
2143 UpdateVerticalScrollBar ();
2147 RepositionScrollBars ();
2150 /* Determines if the horizontal scrollbar has to be displyed */
2151 private bool UpdateHorizontalScrollBar ()
2154 bool enabled = true;
2157 if (canvas_size.Width > items_area.Width) {
2159 hscrollbar.Maximum = canvas_size.Width / ColumnWidthInternal - 1;
2160 } else if (ScrollAlwaysVisible == true) {
2163 hscrollbar.Maximum = 0;
2165 } else if (canvas_size.Width > ClientRectangle.Width && HorizontalScrollbar) {
2167 hscrollbar.Maximum = canvas_size.Width;
2168 hscrollbar.LargeChange = Math.Max (0, items_area.Width);
2169 } else if (scroll_always_visible && horizontal_scrollbar) {
2172 hscrollbar.Maximum = 0;
2175 hbar_offset = hscrollbar.Value;
2176 hscrollbar.Enabled = enabled;
2177 hscrollbar.Visible = show;
2182 /* Determines if the vertical scrollbar has to be displyed */
2183 private bool UpdateVerticalScrollBar ()
2185 if (MultiColumn || (Items.Count == 0 && !scroll_always_visible)) {
2186 vscrollbar.Visible = false;
2188 } else if (Items.Count == 0) {
2189 vscrollbar.Visible = true;
2190 vscrollbar.Enabled = false;
2191 vscrollbar.Maximum = 0;
2196 bool enabled = true;
2197 if (canvas_size.Height > items_area.Height) {
2199 vscrollbar.Maximum = Items.Count - 1;
2200 vscrollbar.LargeChange = Math.Max (items_area.Height / ItemHeight, 0);
2201 } else if (ScrollAlwaysVisible) {
2204 vscrollbar.Maximum = 0;
2207 vscrollbar.Enabled = enabled;
2208 vscrollbar.Visible = show;
2214 private void VerticalScrollEvent (object sender, EventArgs e)
2216 int top_item = top_index;
2218 top_index = /*row_count + */ vscrollbar.Value;
2219 last_visible_index = LastVisibleItem ();
2221 int delta = (top_item - top_index) * ItemHeight;
2222 if (DrawMode == DrawMode.OwnerDrawVariable) {
2225 if (top_index < top_item)
2226 for (int i = top_index; i < top_item; i++)
2227 delta += GetItemHeight (i);
2229 for (int i = top_item; i < top_index; i++)
2230 delta -= GetItemHeight (i);
2233 if (IsHandleCreated)
2234 XplatUI.ScrollWindow (Handle, items_area, 0, delta, false);
2237 #endregion Private Methods
2239 public class IntegerCollection : IList, ICollection, IEnumerable
2241 private ListBox owner;
2242 private List<int> list;
2244 #region Public Constructor
2245 public IntegerCollection (ListBox owner)
2248 list = new List<int> ();
2252 #region Public Properties
2255 get { return list.Count; }
2258 public int this [int index] {
2259 get { return list[index]; }
2260 set { list[index] = value; owner.CalculateTabStops (); }
2264 #region Public Methods
2265 public int Add (int item)
2267 // This collection does not allow duplicates
2268 if (!list.Contains (item)) {
2271 owner.CalculateTabStops ();
2274 return list.IndexOf (item);
2277 public void AddRange (int[] items)
2282 public void AddRange (IntegerCollection value)
2287 void AddItems (IList items)
2290 throw new ArgumentNullException ("items");
2292 foreach (int i in items)
2293 if (!list.Contains (i))
2299 public void Clear ()
2302 owner.CalculateTabStops ();
2305 public bool Contains (int item)
2307 return list.Contains (item);
2310 public void CopyTo (Array destination, int index)
2312 for (int i = 0; i < list.Count; i++)
2313 destination.SetValue (list[i], index++);
2316 public int IndexOf (int item)
2318 return list.IndexOf (item);
2321 public void Remove (int item)
2325 owner.CalculateTabStops ();
2328 public void RemoveAt (int index)
2331 throw new IndexOutOfRangeException ();
2333 list.RemoveAt (index);
2335 owner.CalculateTabStops ();
2339 #region IEnumerable Members
2340 IEnumerator IEnumerable.GetEnumerator ()
2342 return list.GetEnumerator ();
2346 #region IList Members
2347 int IList.Add (object item)
2349 int? intValue = item as int?;
2350 if (!intValue.HasValue)
2351 throw new ArgumentException ("item");
2352 return Add (intValue.Value);
2360 bool IList.Contains (object item)
2362 int? intValue = item as int?;
2363 if (!intValue.HasValue)
2365 return Contains (intValue.Value);
2368 int IList.IndexOf (object item)
2370 int? intValue = item as int?;
2371 if (!intValue.HasValue)
2373 return IndexOf (intValue.Value);
2376 void IList.Insert (int index, object value)
2378 throw new NotSupportedException (string.Format (
2379 CultureInfo.InvariantCulture, "No items "
2380 + "can be inserted into {0}, since it is"
2381 + " a sorted collection.", this.GetType ()));
2384 bool IList.IsFixedSize
2386 get { return false; }
2389 bool IList.IsReadOnly
2391 get { return false; }
2394 void IList.Remove (object value)
2396 int? intValue = value as int?;
2397 if (!intValue.HasValue)
2398 throw new ArgumentException ("value");
2400 Remove (intValue.Value);
2403 void IList.RemoveAt (int index)
2408 object IList.this[int index] {
2409 get { return this[index]; }
2410 set { this[index] = (int)value; }
2414 #region ICollection Members
2415 bool ICollection.IsSynchronized {
2416 get { return true; }
2419 object ICollection.SyncRoot {
2420 get { return this; }
2425 [ListBindable (false)]
2426 public class ObjectCollection : IList, ICollection, IEnumerable
2428 internal class ListObjectComparer : IComparer
2430 public int Compare (object a, object b)
2432 string str1 = a.ToString ();
2433 string str2 = b.ToString ();
2434 return str1.CompareTo (str2);
2438 private ListBox owner;
2439 internal ArrayList object_items = new ArrayList ();
2441 #region UIA Framework Events
2443 // We are using Reflection to add/remove internal events.
2444 // Class ListProvider uses the events.
2446 //Event used to generate UIA StructureChangedEvent
2447 static object UIACollectionChangedEvent = new object ();
2449 internal event CollectionChangeEventHandler UIACollectionChanged {
2450 add { owner.Events.AddHandler (UIACollectionChangedEvent, value); }
2451 remove { owner.Events.RemoveHandler (UIACollectionChangedEvent, value); }
2454 internal void OnUIACollectionChangedEvent (CollectionChangeEventArgs args)
2456 CollectionChangeEventHandler eh
2457 = (CollectionChangeEventHandler) owner.Events [UIACollectionChangedEvent];
2461 #endregion UIA Framework Events
2463 public ObjectCollection (ListBox owner)
2468 public ObjectCollection (ListBox owner, object[] value)
2474 public ObjectCollection (ListBox owner, ObjectCollection value)
2480 #region Public Properties
2482 get { return object_items.Count; }
2485 public bool IsReadOnly {
2486 get { return false; }
2490 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2491 public virtual object this [int index] {
2493 if (index < 0 || index >= Count)
2494 throw new ArgumentOutOfRangeException ("Index of out range");
2496 return object_items[index];
2499 if (index < 0 || index >= Count)
2500 throw new ArgumentOutOfRangeException ("Index of out range");
2502 throw new ArgumentNullException ("value");
2504 //UIA Framework event: Item Removed
2505 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, object_items [index]));
2507 object_items[index] = value;
2509 //UIA Framework event: Item Added
2510 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, value));
2512 owner.CollectionChanged ();
2516 bool ICollection.IsSynchronized {
2517 get { return false; }
2520 object ICollection.SyncRoot {
2521 get { return this; }
2524 bool IList.IsFixedSize {
2525 get { return false; }
2528 #endregion Public Properties
2530 #region Public Methods
2531 public int Add (object item)
2535 idx = AddItem (item);
2536 owner.CollectionChanged ();
2538 // If we are sorted, the item probably moved indexes, get the real one
2540 return this.IndexOf (item);
2545 public void AddRange (object[] items)
2550 public void AddRange (ObjectCollection value)
2555 internal void AddItems (IList items)
2558 throw new ArgumentNullException ("items");
2560 foreach (object mi in items)
2563 owner.CollectionChanged ();
2566 public virtual void Clear ()
2568 owner.selected_indices.ClearCore ();
2569 object_items.Clear ();
2570 owner.CollectionChanged ();
2572 //UIA Framework event: Items list cleared
2573 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, null));
2576 public bool Contains (object value)
2579 throw new ArgumentNullException ("value");
2581 return object_items.Contains (value);
2584 public void CopyTo (object[] destination, int arrayIndex)
2586 object_items.CopyTo (destination, arrayIndex);
2589 void ICollection.CopyTo (Array destination, int index)
2591 object_items.CopyTo (destination, index);
2594 public IEnumerator GetEnumerator ()
2596 return object_items.GetEnumerator ();
2599 int IList.Add (object item)
2604 public int IndexOf (object value)
2607 throw new ArgumentNullException ("value");
2609 return object_items.IndexOf (value);
2612 public void Insert (int index, object item)
2614 if (index < 0 || index > Count)
2615 throw new ArgumentOutOfRangeException ("Index of out range");
2617 throw new ArgumentNullException ("item");
2619 owner.BeginUpdate ();
2620 object_items.Insert (index, item);
2621 owner.CollectionChanged ();
2624 //UIA Framework event: Item Added
2625 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, item));
2628 public void Remove (object value)
2633 int index = IndexOf (value);
2638 public void RemoveAt (int index)
2640 if (index < 0 || index >= Count)
2641 throw new ArgumentOutOfRangeException ("Index of out range");
2643 //UIA Framework element removed
2644 object removed = object_items [index];
2645 UpdateSelection (index);
2646 object_items.RemoveAt (index);
2647 owner.CollectionChanged ();
2649 //UIA Framework event: Item Removed
2650 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, removed));
2652 #endregion Public Methods
2654 #region Private Methods
2655 internal int AddItem (object item)
2658 throw new ArgumentNullException ("item");
2660 int cnt = object_items.Count;
2661 object_items.Add (item);
2663 //UIA Framework event: Item Added
2664 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, item));
2669 // we receive the index to be removed
2670 void UpdateSelection (int removed_index)
2672 owner.selected_indices.Remove (removed_index);
2674 if (owner.selection_mode != SelectionMode.None) {
2675 int last_idx = object_items.Count - 1;
2677 // if the last item was selected, remove it from selection,
2678 // since it will become invalid after the removal
2679 if (owner.selected_indices.Contains (last_idx)) {
2680 owner.selected_indices.Remove (last_idx);
2682 // in SelectionMode.One try to put the selection on the new last item
2683 int new_idx = last_idx - 1;
2684 if (owner.selection_mode == SelectionMode.One && new_idx > -1)
2685 owner.selected_indices.Add (new_idx);
2691 internal void Sort ()
2693 object_items.Sort (new ListObjectComparer ());
2696 #endregion Private Methods
2699 public class SelectedIndexCollection : IList, ICollection, IEnumerable
2701 private ListBox owner;
2702 ArrayList selection;
2703 bool sorting_needed; // Selection state retrieval is done sorted - we do it lazyly
2705 #region UIA Framework Events
2708 // We are using Reflection to add/remove internal events.
2709 // Class ListProvider uses the events.
2711 //Event used to generate UIA StructureChangedEvent
2712 static object UIACollectionChangedEvent = new object ();
2714 internal event CollectionChangeEventHandler UIACollectionChanged {
2715 add { owner.Events.AddHandler (UIACollectionChangedEvent, value); }
2716 remove { owner.Events.RemoveHandler (UIACollectionChangedEvent, value); }
2719 internal void OnUIACollectionChangedEvent (CollectionChangeEventArgs args)
2721 CollectionChangeEventHandler eh
2722 = (CollectionChangeEventHandler) owner.Events [UIACollectionChangedEvent];
2727 #endregion UIA Framework Events
2730 public SelectedIndexCollection (ListBox owner)
2733 selection = new ArrayList ();
2736 #region Public Properties
2739 get { return selection.Count; }
2742 public bool IsReadOnly {
2743 get { return true; }
2746 public int this [int index] {
2748 if (index < 0 || index >= Count)
2749 throw new ArgumentOutOfRangeException ("Index of out range");
2752 return (int)selection [index];
2756 bool ICollection.IsSynchronized {
2757 get { return true; }
2760 bool IList.IsFixedSize{
2761 get { return true; }
2764 object ICollection.SyncRoot {
2765 get { return selection; }
2768 #endregion Public Properties
2770 #region Public Methods
2771 public void Add (int index)
2773 if (AddCore (index)) {
2774 owner.OnSelectedIndexChanged (EventArgs.Empty);
2775 owner.OnSelectedValueChanged (EventArgs.Empty);
2779 // Need to separate selection logic from events,
2780 // since selection changes using keys/mouse handle them their own way
2781 internal bool AddCore (int index)
2783 if (selection.Contains (index))
2786 if (index == -1) // Weird MS behaviour
2788 if (index < -1 || index >= owner.Items.Count)
2789 throw new ArgumentOutOfRangeException ("index");
2790 if (owner.selection_mode == SelectionMode.None)
2791 throw new InvalidOperationException ("Cannot call this method when selection mode is SelectionMode.None");
2793 if (owner.selection_mode == SelectionMode.One && Count > 0) // Unselect previously selected item
2794 RemoveCore ((int)selection [0]);
2796 selection.Add (index);
2797 sorting_needed = true;
2798 owner.EnsureVisible (index);
2799 owner.FocusedItem = index;
2800 owner.InvalidateItem (index);
2802 // UIA Framework event: Selected item added
2803 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, index));
2808 public void Clear ()
2811 owner.OnSelectedIndexChanged (EventArgs.Empty);
2812 owner.OnSelectedValueChanged (EventArgs.Empty);
2816 internal bool ClearCore ()
2818 if (selection.Count == 0)
2821 foreach (int index in selection)
2822 owner.InvalidateItem (index);
2826 // UIA Framework event: Selected items list updated
2827 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, -1));
2832 public bool Contains (int selectedIndex)
2834 foreach (int index in selection)
2835 if (index == selectedIndex)
2840 public void CopyTo (Array destination, int index)
2843 selection.CopyTo (destination, index);
2846 public IEnumerator GetEnumerator ()
2849 return selection.GetEnumerator ();
2852 // FIXME: Probably we can avoid sorting when calling
2853 // IndexOf (imagine a scenario where multiple removal of items
2855 public void Remove (int index)
2857 // Separate logic from events here too
2858 if (RemoveCore (index)) {
2859 owner.OnSelectedIndexChanged (EventArgs.Empty);
2860 owner.OnSelectedValueChanged (EventArgs.Empty);
2864 internal bool RemoveCore (int index)
2866 int idx = IndexOf (index);
2870 selection.RemoveAt (idx);
2871 owner.InvalidateItem (index);
2873 // UIA Framework event: Selected item removed from selection
2874 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, index));
2879 int IList.Add (object value)
2881 throw new NotSupportedException ();
2886 throw new NotSupportedException ();
2889 bool IList.Contains (object selectedIndex)
2891 return Contains ((int)selectedIndex);
2894 int IList.IndexOf (object selectedIndex)
2896 return IndexOf ((int) selectedIndex);
2899 void IList.Insert (int index, object value)
2901 throw new NotSupportedException ();
2904 void IList.Remove (object value)
2906 throw new NotSupportedException ();
2909 void IList.RemoveAt (int index)
2911 throw new NotSupportedException ();
2914 object IList.this[int index]{
2915 get { return this [index]; }
2916 set {throw new NotImplementedException (); }
2919 public int IndexOf (int selectedIndex)
2923 for (int i = 0; i < selection.Count; i++)
2924 if ((int)selection [i] == selectedIndex)
2929 #endregion Public Methods
2930 internal ArrayList List {
2939 if (sorting_needed) {
2940 sorting_needed = false;
2946 public class SelectedObjectCollection : IList, ICollection, IEnumerable
2948 private ListBox owner;
2950 public SelectedObjectCollection (ListBox owner)
2955 #region Public Properties
2957 get { return owner.selected_indices.Count; }
2960 public bool IsReadOnly {
2961 get { return true; }
2965 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2966 public object this [int index] {
2968 if (index < 0 || index >= Count)
2969 throw new ArgumentOutOfRangeException ("Index of out range");
2971 return owner.items [owner.selected_indices [index]];
2973 set {throw new NotSupportedException ();}
2976 bool ICollection.IsSynchronized {
2977 get { return true; }
2980 object ICollection.SyncRoot {
2981 get { return this; }
2984 bool IList.IsFixedSize {
2985 get { return true; }
2988 #endregion Public Properties
2990 #region Public Methods
2991 public void Add (object value)
2993 if (owner.selection_mode == SelectionMode.None)
2994 throw new ArgumentException ("Cannot call this method if SelectionMode is SelectionMode.None");
2996 int idx = owner.items.IndexOf (value);
3000 owner.selected_indices.Add (idx);
3003 public void Clear ()
3005 owner.selected_indices.Clear ();
3008 public bool Contains (object selectedObject)
3010 int idx = owner.items.IndexOf (selectedObject);
3011 return idx == -1 ? false : owner.selected_indices.Contains (idx);
3014 public void CopyTo (Array destination, int index)
3016 for (int i = 0; i < Count; i++)
3017 destination.SetValue (this [i], index++);
3020 public void Remove (object value)
3025 int idx = owner.items.IndexOf (value);
3029 owner.selected_indices.Remove (idx);
3032 int IList.Add (object value)
3034 throw new NotSupportedException ();
3039 throw new NotSupportedException ();
3042 void IList.Insert (int index, object value)
3044 throw new NotSupportedException ();
3047 void IList.Remove (object value)
3049 throw new NotSupportedException ();
3052 void IList.RemoveAt (int index)
3054 throw new NotSupportedException ();
3057 public int IndexOf (object selectedObject)
3059 int idx = owner.items.IndexOf (selectedObject);
3060 return idx == -1 ? -1 : owner.selected_indices.IndexOf (idx);
3063 public IEnumerator GetEnumerator ()
3065 //FIXME: write an enumerator that uses selection.GetEnumerator
3066 // so that invalidation is write on selection changes
3067 object [] items = new object [Count];
3068 for (int i = 0; i < Count; i++) {
3069 items [i] = owner.items [owner.selected_indices [i]];
3072 return items.GetEnumerator ();
3075 #endregion Public Methods