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.Reflection;
36 using System.Runtime.InteropServices;
38 namespace System.Windows.Forms
40 [DefaultProperty("Items")]
41 [DefaultEvent("SelectedIndexChanged")]
42 [Designer ("System.Windows.Forms.Design.ListBoxDesigner, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.IDesigner")]
43 public class ListBox : ListControl
45 public const int DefaultItemHeight = 13;
46 public const int NoMatches = -1;
48 internal enum ItemNavigation
60 Hashtable item_heights;
61 private int item_height = -1;
62 private int column_width = 0;
63 private int requested_height = -1;
64 private DrawMode draw_mode = DrawMode.Normal;
65 private int horizontal_extent = 0;
66 private bool horizontal_scrollbar = false;
67 private bool integral_height = true;
68 private bool multicolumn = false;
69 private bool scroll_always_visible = false;
70 private int selected_index = -1;
71 private SelectedIndexCollection selected_indices;
72 private SelectedObjectCollection selected_items;
73 private ArrayList selection = new ArrayList ();
74 private ArrayList display_selection = new ArrayList ();
75 private SelectionMode selection_mode = SelectionMode.One;
76 private bool sorted = false;
77 private bool use_tabstops = true;
78 private int column_width_internal = 120;
79 private ImplicitVScrollBar vscrollbar;
80 private ImplicitHScrollBar hscrollbar;
81 private int hbar_offset;
82 private bool suspend_layout;
83 private bool ctrl_pressed = false;
84 private bool shift_pressed = false;
85 private bool has_focus = false;
86 private bool explicit_item_height = false;
87 private int top_index = 0;
88 private int last_visible_index = 0;
89 private Rectangle items_area;
90 private int focused_item = -1;
91 private ObjectCollection items;
95 border_style = BorderStyle.Fixed3D;
96 BackColor = ThemeEngine.Current.ColorWindow;
98 items = CreateItemCollection ();
99 selected_indices = new SelectedIndexCollection (this);
100 selected_items = new SelectedObjectCollection (this);
102 /* Vertical scrollbar */
103 vscrollbar = new ImplicitVScrollBar ();
104 vscrollbar.Minimum = 0;
105 vscrollbar.SmallChange = 1;
106 vscrollbar.LargeChange = 1;
107 vscrollbar.Maximum = 0;
108 vscrollbar.ValueChanged += new EventHandler (VerticalScrollEvent);
109 vscrollbar.Visible = false;
111 /* Horizontal scrollbar */
112 hscrollbar = new ImplicitHScrollBar ();
113 hscrollbar.Minimum = 0;
114 hscrollbar.SmallChange = 1;
115 hscrollbar.LargeChange = 1;
116 hscrollbar.Maximum = 0;
117 hscrollbar.Visible = false;
118 hscrollbar.ValueChanged += new EventHandler (HorizontalScrollEvent);
121 MouseDown += new MouseEventHandler (OnMouseDownLB);
122 MouseMove += new MouseEventHandler (OnMouseMoveLB);
123 MouseUp += new MouseEventHandler (OnMouseUpLB);
124 MouseWheel += new MouseEventHandler (OnMouseWheelLB);
125 KeyDown += new KeyEventHandler (OnKeyDownLB);
126 KeyUp += new KeyEventHandler (OnKeyUpLB);
127 GotFocus += new EventHandler (OnGotFocus);
128 LostFocus += new EventHandler (OnLostFocus);
130 SetStyle (ControlStyles.UserPaint, false);
135 [EditorBrowsable (EditorBrowsableState.Never)]
136 public new event EventHandler BackgroundImageChanged {
137 add { base.BackgroundImageChanged += value; }
138 remove { base.BackgroundImageChanged -= value; }
142 [EditorBrowsable (EditorBrowsableState.Advanced)]
143 public new event EventHandler Click {
144 add { base.Click += value; }
145 remove { base.Click -= value; }
148 public event DrawItemEventHandler DrawItem;
149 public event MeasureItemEventHandler MeasureItem;
152 [EditorBrowsable (EditorBrowsableState.Never)]
153 public new event PaintEventHandler Paint {
154 add { base.Paint += value; }
155 remove { base.Paint -= value; }
158 public event EventHandler SelectedIndexChanged;
161 [EditorBrowsable (EditorBrowsableState.Advanced)]
162 public new event EventHandler TextChanged {
163 add { base.TextChanged += value; }
164 remove { base.TextChanged -= value; }
168 #region Public Properties
169 public override Color BackColor {
170 get { return base.BackColor; }
172 if (base.BackColor == value)
175 base.BackColor = value;
176 base.Refresh (); // Careful. Calling the base method is not the same that calling
177 } // the overriden one that refresh also all the items
181 [EditorBrowsable (EditorBrowsableState.Never)]
182 public override Image BackgroundImage {
183 get { return base.BackgroundImage; }
185 base.BackgroundImage = value;
190 [DefaultValue (BorderStyle.Fixed3D)]
192 public BorderStyle BorderStyle {
193 get { return InternalBorderStyle; }
195 InternalBorderStyle = value;
202 public int ColumnWidth {
203 get { return column_width; }
206 throw new ArgumentException ("A value less than zero is assigned to the property.");
208 column_width = value;
211 ColumnWidthInternal = 120;
213 ColumnWidthInternal = value;
219 protected override CreateParams CreateParams {
220 get { return base.CreateParams;}
223 protected override Size DefaultSize {
224 get { return new Size (120, 96); }
227 [RefreshProperties(RefreshProperties.Repaint)]
228 [DefaultValue (DrawMode.Normal)]
229 public virtual DrawMode DrawMode {
230 get { return draw_mode; }
233 if (!Enum.IsDefined (typeof (DrawMode), value))
234 throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for DrawMode", value));
236 if (value == DrawMode.OwnerDrawVariable && multicolumn == true)
237 throw new ArgumentException ("Cannot have variable height and multicolumn");
239 if (draw_mode == value)
244 if (draw_mode == DrawMode.OwnerDrawVariable)
245 item_heights = new Hashtable ();
253 public override Color ForeColor {
254 get { return base.ForeColor; }
257 if (base.ForeColor == value)
260 base.ForeColor = value;
267 public int HorizontalExtent {
268 get { return horizontal_extent; }
270 if (horizontal_extent == value)
273 horizontal_extent = value;
278 [DefaultValue (false)]
280 public bool HorizontalScrollbar {
281 get { return horizontal_scrollbar; }
283 if (horizontal_scrollbar == value)
286 horizontal_scrollbar = value;
292 [DefaultValue (true)]
294 [RefreshProperties(RefreshProperties.Repaint)]
295 public bool IntegralHeight {
296 get { return integral_height; }
298 if (integral_height == value)
301 integral_height = value;
308 [RefreshProperties(RefreshProperties.Repaint)]
309 public virtual int ItemHeight {
311 if (item_height == -1) {
312 SizeF sz = DeviceContext.MeasureString ("The quick brown Fox", Font);
313 item_height = (int) sz.Height;
319 throw new ArgumentOutOfRangeException ("The ItemHeight property was set beyond 255 pixels");
321 explicit_item_height = true;
322 if (item_height == value)
332 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)]
334 [Editor ("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
335 public ObjectCollection Items {
336 get { return items; }
339 [DefaultValue (false)]
340 public bool MultiColumn {
341 get { return multicolumn; }
343 if (multicolumn == value)
346 if (value == true && DrawMode == DrawMode.OwnerDrawVariable)
347 throw new ArgumentException ("A multicolumn ListBox cannot have a variable-sized height.");
355 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
356 [EditorBrowsable (EditorBrowsableState.Advanced)]
357 public int PreferredHeight {
360 if (draw_mode == DrawMode.Normal)
361 itemsHeight = FontHeight * items.Count;
362 else if (draw_mode == DrawMode.OwnerDrawFixed)
363 itemsHeight = ItemHeight * items.Count;
364 else if (draw_mode == DrawMode.OwnerDrawVariable) {
365 for (int i = 0; i < items.Count; i++)
366 itemsHeight += (int) item_heights [Items [i]];
373 public override RightToLeft RightToLeft {
374 get { return base.RightToLeft; }
376 base.RightToLeft = value;
377 if (base.RightToLeft == RightToLeft.Yes)
378 StringFormat.Alignment = StringAlignment.Far;
380 StringFormat.Alignment = StringAlignment.Near;
385 // Only affects the Vertical ScrollBar
386 [DefaultValue (false)]
388 public bool ScrollAlwaysVisible {
389 get { return scroll_always_visible; }
391 if (scroll_always_visible == value)
394 scroll_always_visible = value;
401 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
402 public override int SelectedIndex {
403 get { return selected_index;}
405 if (value < -1 || value >= Items.Count)
406 throw new ArgumentOutOfRangeException ("Index of out range");
408 if (SelectionMode == SelectionMode.None)
409 throw new ArgumentException ("cannot call this method if SelectionMode is SelectionMode.None");
411 if (selected_index == value)
416 else if (SelectionMode == SelectionMode.One)
417 UnSelectItem (selected_index, true);
420 selected_index = value;
422 OnSelectedIndexChanged (new EventArgs ());
423 OnSelectedValueChanged (new EventArgs ());
428 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
429 public SelectedIndexCollection SelectedIndices {
430 get { return selected_indices; }
435 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
436 public object SelectedItem {
438 if (SelectedItems.Count > 0)
439 return SelectedItems[0];
444 if (value != null && !Items.Contains (value))
445 return; // FIXME: this is probably an exception
447 SelectedIndex = value == null ? - 1 : Items.IndexOf (value);
452 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
453 public SelectedObjectCollection SelectedItems {
454 get {return selected_items;}
457 [DefaultValue (SelectionMode.One)]
458 public virtual SelectionMode SelectionMode {
459 get { return selection_mode; }
461 if (!Enum.IsDefined (typeof (SelectionMode), value))
462 throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for SelectionMode", value));
464 if (selection_mode == value)
467 selection_mode = value;
469 switch (selection_mode) {
470 case SelectionMode.None:
474 case SelectionMode.One:
475 while (SelectedIndices.Count > 1)
476 UnSelectItem (SelectedIndices [SelectedIndices.Count - 1], true);
485 [DefaultValue (false)]
487 get { return sorted; }
501 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
502 [EditorBrowsable (EditorBrowsableState.Advanced)]
503 public override string Text {
505 if (SelectionMode != SelectionMode.None && SelectedIndex != -1)
506 return GetItemText (SelectedItem);
514 if (SelectionMode == SelectionMode.None)
519 index = FindStringExact (value);
524 SelectedIndex = index;
529 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
530 public int TopIndex {
531 get { return top_index; }
533 if (value == top_index)
536 if (value < 0 || value >= Items.Count)
545 [DefaultValue (true)]
546 public bool UseTabStops {
547 get { return use_tabstops; }
550 if (use_tabstops == value)
553 use_tabstops = value;
555 StringFormat.SetTabStops (0, new float [] {(float)(Font.Height * 3.7)});
557 StringFormat.SetTabStops (0, new float [0]);
562 #endregion Public Properties
564 #region Private Properties
566 private int ColumnWidthInternal {
567 get { return column_width_internal; }
568 set { column_width_internal = value; }
571 private int row_count = 1;
572 private int RowCount {
574 return MultiColumn ? row_count : Items.Count;
578 #endregion Private Properties
580 #region Public Methods
581 protected virtual void AddItemsCore (object[] value)
583 Items.AddRange (value);
586 public void BeginUpdate ()
588 suspend_layout = true;
591 public void ClearSelected ()
593 foreach (int i in selected_indices) {
594 UnSelectItem (i, false);
600 protected virtual ObjectCollection CreateItemCollection ()
602 return new ObjectCollection (this);
605 public void EndUpdate ()
607 suspend_layout = false;
612 public int FindString (String s)
614 return FindString (s, -1);
617 public int FindString (string s, int startIndex)
619 if (Items.Count == 0)
620 return -1; // No exception throwing if empty
622 if (startIndex < -1 || startIndex >= Items.Count - 1)
623 throw new ArgumentOutOfRangeException ("Index of out range");
626 for (int i = startIndex; i < Items.Count; i++) {
627 if ((GetItemText (Items[i])).StartsWith (s))
634 public int FindStringExact (string s)
636 return FindStringExact (s, -1);
639 public int FindStringExact (string s, int startIndex)
641 if (Items.Count == 0)
642 return -1; // No exception throwing if empty
644 if (startIndex < -1 || startIndex >= Items.Count - 1)
645 throw new ArgumentOutOfRangeException ("Index of out range");
648 for (int i = startIndex; i < Items.Count; i++) {
649 if ((GetItemText (Items[i])).Equals (s))
656 public int GetItemHeight (int index)
658 if (index < 0 || index >= Items.Count)
659 throw new ArgumentOutOfRangeException ("Index of out range");
661 if (DrawMode == DrawMode.OwnerDrawVariable && IsHandleCreated == true) {
663 object o = Items [index];
664 if (item_heights.Contains (o))
665 return (int) item_heights [o];
667 MeasureItemEventArgs args = new MeasureItemEventArgs (DeviceContext, index, ItemHeight);
668 OnMeasureItem (args);
669 item_heights [o] = args.ItemHeight;
670 return args.ItemHeight;
676 public Rectangle GetItemRectangle (int index)
678 if (index < 0 || index >= Items.Count)
679 throw new ArgumentOutOfRangeException ("GetItemRectangle index out of range.");
681 Rectangle rect = new Rectangle ();
684 int col = index / RowCount;
685 rect.Y = ((index - top_index) % RowCount) * ItemHeight;
686 rect.X = col * ColumnWidthInternal;
687 rect.Height = ItemHeight;
688 rect.Width = ColumnWidthInternal;
691 rect.Height = GetItemHeight (index);
692 rect.Width = items_area.Width;
694 if (DrawMode == DrawMode.OwnerDrawVariable) {
696 if (index >= top_index) {
697 for (int i = top_index; i < index; i++) {
698 rect.Y += GetItemHeight (i);
701 for (int i = index; i < top_index; i++) {
702 rect.Y -= GetItemHeight (i);
706 rect.Y = ItemHeight * (index - top_index);
713 public bool GetSelected (int index)
715 if (index < 0 || index >= Items.Count)
716 throw new ArgumentOutOfRangeException ("Index of out range");
718 return SelectedIndices.Contains (index);
721 public int IndexFromPoint (Point p)
723 return IndexFromPoint (p.X, p.Y);
726 // Only returns visible points
727 public int IndexFromPoint (int x, int y)
730 if (Items.Count == 0) {
734 for (int i = top_index; i <= last_visible_index; i++) {
735 if (GetItemRectangle (i).Contains (x,y) == true)
742 protected override void OnChangeUICues (UICuesEventArgs e)
744 base.OnChangeUICues (e);
747 protected override void OnDataSourceChanged (EventArgs e)
749 base.OnDataSourceChanged (e);
752 if (DataSource == null || DataManager == null) {
756 SelectedIndex = DataManager.Position;
760 protected override void OnDisplayMemberChanged (EventArgs e)
762 base.OnDisplayMemberChanged (e);
764 if (DataManager == null || !IsHandleCreated)
771 protected virtual void OnDrawItem (DrawItemEventArgs e)
774 case DrawMode.OwnerDrawFixed:
775 case DrawMode.OwnerDrawVariable:
776 if (DrawItem != null)
781 ThemeEngine.Current.DrawListBoxItem (this, e);
786 protected override void OnFontChanged (EventArgs e)
788 base.OnFontChanged (e);
791 StringFormat.SetTabStops (0, new float [] {(float)(Font.Height * 3.7)});
793 if (explicit_item_height) {
796 SizeF sz = DeviceContext.MeasureString ("The quick brown Fox", Font);
797 item_height = (int) sz.Height;
804 protected override void OnHandleCreated (EventArgs e)
806 base.OnHandleCreated (e);
809 Controls.AddImplicit (vscrollbar);
810 Controls.AddImplicit (hscrollbar);
815 protected override void OnHandleDestroyed (EventArgs e)
817 base.OnHandleDestroyed (e);
820 protected virtual void OnMeasureItem (MeasureItemEventArgs e)
822 if (draw_mode != DrawMode.OwnerDrawVariable)
825 if (MeasureItem != null)
826 MeasureItem (this, e);
829 protected override void OnParentChanged (EventArgs e)
831 base.OnParentChanged (e);
834 protected override void OnResize (EventArgs e)
837 if (canvas_size.IsEmpty || MultiColumn)
841 protected override void OnSelectedIndexChanged (EventArgs e)
843 base.OnSelectedIndexChanged (e);
845 if (SelectedIndexChanged != null)
846 SelectedIndexChanged (this, e);
849 protected override void OnSelectedValueChanged (EventArgs e)
851 base.OnSelectedValueChanged (e);
854 public override void Refresh ()
856 if (draw_mode == DrawMode.OwnerDrawVariable)
857 item_heights.Clear ();
862 protected override void RefreshItem (int index)
864 if (index < 0 || index >= Items.Count)
865 throw new ArgumentOutOfRangeException ("Index of out range");
867 if (draw_mode == DrawMode.OwnerDrawVariable)
868 item_heights.Remove (Items [index]);
871 protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
873 if ((specified & BoundsSpecified.Height) == BoundsSpecified.Height)
874 requested_height = height;
876 if (IntegralHeight) {
878 switch (border_style) {
879 case BorderStyle.Fixed3D:
880 border = ThemeEngine.Current.Border3DSize.Height;
882 case BorderStyle.FixedSingle:
883 border = ThemeEngine.Current.BorderSize.Height;
885 case BorderStyle.None:
890 height -= (2 * border);
891 height -= height % ItemHeight;
892 height += (2 * border);
895 base.SetBoundsCore (x, y, width, height, specified);
899 protected override void SetItemCore (int index, object value)
901 if (index < 0 || index >= Items.Count)
904 Items[index] = value;
907 protected override void SetItemsCore (IList value)
912 Items.AddRange (value);
918 public void SetSelected (int index, bool value)
920 if (index < 0 || index >= Items.Count)
921 throw new ArgumentOutOfRangeException ("Index of out range");
923 if (SelectionMode == SelectionMode.None)
924 throw new InvalidOperationException ();
929 UnSelectItem (index, true);
932 protected virtual void Sort ()
934 if (Items.Count == 0)
941 public override string ToString ()
943 return base.ToString ();
946 protected virtual void WmReflectCommand (ref Message m)
950 protected override void WndProc (ref Message m)
952 base.WndProc (ref m);
955 #endregion Public Methods
957 #region Private Methods
959 private Size canvas_size;
961 private void Layout ()
963 if (!IsHandleCreated || suspend_layout)
967 LayoutMultiColumn ();
969 LayoutSingleColumn ();
972 last_visible_index = LastVisibleItem ();
975 private void LayoutSingleColumn ()
980 case DrawMode.OwnerDrawVariable:
982 width = HorizontalExtent;
983 for (int i = 0; i < Items.Count; i++) {
984 height += GetItemHeight (i);
988 case DrawMode.OwnerDrawFixed:
989 height = Items.Count * ItemHeight;
990 width = HorizontalExtent;
993 case DrawMode.Normal:
995 height = Items.Count * ItemHeight;
997 for (int i = 0; i < Items.Count; i++) {
998 SizeF sz = DeviceContext.MeasureString (GetItemText (Items[i]), Font);
999 if ((int) sz.Width > width)
1000 width = (int) sz.Width;
1005 canvas_size = new Size (width, height);
1008 private void LayoutMultiColumn ()
1010 int usable_height = ClientRectangle.Height - (ScrollAlwaysVisible ? hscrollbar.Height : 0);
1011 row_count = usable_height / ItemHeight;
1014 int cols = (int) Math.Ceiling ((float)Items.Count / (float) row_count);
1015 Size sz = new Size (cols * ColumnWidthInternal, row_count * ItemHeight);
1016 if (!ScrollAlwaysVisible && sz.Width > ClientRectangle.Width && row_count > 1) {
1017 usable_height = ClientRectangle.Height - hscrollbar.Height;
1018 row_count = usable_height / ItemHeight;
1019 cols = (int) Math.Ceiling ((float)Items.Count / (float) row_count);
1020 sz = new Size (cols * ColumnWidthInternal, row_count * ItemHeight);
1025 internal void Draw (Rectangle clip, Graphics dc)
1027 Theme theme = ThemeEngine.Current;
1029 if (hscrollbar.Visible && vscrollbar.Visible) {
1030 // Paint the dead space in the bottom right corner
1031 Rectangle rect = new Rectangle (hscrollbar.Right, vscrollbar.Bottom, vscrollbar.Width, hscrollbar.Height);
1032 if (rect.IntersectsWith (clip))
1033 dc.FillRectangle (theme.ResPool.GetSolidBrush (theme.ColorControl), rect);
1036 dc.FillRectangle (theme.ResPool.GetSolidBrush (BackColor), items_area);
1038 if (Items.Count == 0)
1041 for (int i = top_index; i <= last_visible_index; i++) {
1042 Rectangle rect = GetItemDisplayRectangle (i, top_index);
1044 if (!clip.IntersectsWith (rect))
1047 DrawItemState state = DrawItemState.None;
1049 if (SelectedIndices.Contains (i))
1050 state |= DrawItemState.Selected;
1052 if (has_focus && FocusedItem == i)
1053 state |= DrawItemState.Focus;
1055 if (MultiColumn == false && hscrollbar != null && hscrollbar.Visible) {
1056 rect.X -= hscrollbar.Value;
1057 rect.Width += hscrollbar.Value;
1060 OnDrawItem (new DrawItemEventArgs (dc, Font, rect, i, state, ForeColor, BackColor));
1064 // Converts a GetItemRectangle to a one that we can display
1065 internal Rectangle GetItemDisplayRectangle (int index, int first_displayble)
1067 Rectangle item_rect;
1068 Rectangle first_item_rect = GetItemRectangle (first_displayble);
1069 item_rect = GetItemRectangle (index);
1070 item_rect.X -= first_item_rect.X;
1071 item_rect.Y -= first_item_rect.Y;
1077 private void HorizontalScrollEvent (object sender, EventArgs e)
1080 int top_item = top_index;
1081 int last_item = last_visible_index;
1083 top_index = RowCount * hscrollbar.Value;
1084 last_visible_index = LastVisibleItem ();
1086 if (top_item != top_index || last_item != last_visible_index)
1087 Invalidate (items_area);
1090 int old_offset = hbar_offset;
1091 hbar_offset = hscrollbar.Value;
1093 if (hbar_offset < 0)
1096 XplatUI.ScrollWindow (Handle, items_area, old_offset - hbar_offset, 0, false);
1100 // Only returns visible points. The diference of with IndexFromPoint is that the rectangle
1101 // has screen coordinates
1102 private int IndexAtClientPoint (int x, int y)
1104 if (Items.Count == 0)
1109 else if (x > ClientRectangle.Right)
1110 x = ClientRectangle.Right;
1114 else if (y > ClientRectangle.Bottom)
1115 y = ClientRectangle.Bottom;
1117 for (int i = top_index; i <= last_visible_index; i++)
1118 if (GetItemDisplayRectangle (i, top_index).Contains (x, y))
1124 private int LastVisibleItem ()
1126 Rectangle item_rect;
1127 int top_y = items_area.Y + items_area.Height;
1130 if (top_index >= Items.Count)
1133 for (i = top_index; i < Items.Count; i++) {
1135 item_rect = GetItemDisplayRectangle (i, top_index);
1138 if (item_rect.X > items_area.Width)
1142 if (item_rect.Y + item_rect.Height > top_y) {
1150 private void UpdateTopItem ()
1153 int col = top_index / RowCount;
1155 if (col > hscrollbar.Maximum)
1156 hscrollbar.Value = hscrollbar.Maximum;
1158 hscrollbar.Value = col;
1160 int val = vscrollbar.Value;
1161 if (top_index > vscrollbar.Maximum)
1162 vscrollbar.Value = vscrollbar.Maximum;
1164 vscrollbar.Value = top_index;
1165 Scroll (vscrollbar, vscrollbar.Value - top_index);
1166 XplatUI.ScrollWindow (Handle, items_area, 0, ItemHeight * (val - vscrollbar.Value), false);
1170 // Navigates to the indicated item and returns the new item
1171 private int NavigateItemVisually (ItemNavigation navigation)
1173 int page_size, columns, selected_index = -1;
1176 columns = items_area.Width / ColumnWidthInternal;
1177 page_size = columns * RowCount;
1178 if (page_size == 0) {
1179 page_size = RowCount;
1182 page_size = items_area.Height / ItemHeight;
1185 switch (navigation) {
1187 case ItemNavigation.PreviousColumn: {
1188 if (FocusedItem - RowCount < 0) {
1192 if (FocusedItem - RowCount < top_index) {
1193 top_index = FocusedItem - RowCount;
1197 selected_index = FocusedItem - RowCount;
1201 case ItemNavigation.NextColumn: {
1202 if (FocusedItem + RowCount >= Items.Count) {
1206 if (FocusedItem + RowCount > last_visible_index) {
1207 top_index = FocusedItem;
1211 selected_index = FocusedItem + RowCount;
1215 case ItemNavigation.First: {
1222 case ItemNavigation.Last: {
1224 int rows = items_area.Height / ItemHeight;
1225 if (Items.Count < rows) {
1227 selected_index = Items.Count - 1;
1230 top_index = Items.Count - rows;
1231 selected_index = Items.Count - 1;
1237 case ItemNavigation.Next: {
1238 if (FocusedItem == Items.Count - 1)
1242 ArrayList heights = new ArrayList ();
1243 if (draw_mode == DrawMode.OwnerDrawVariable) {
1244 for (int i = top_index; i <= FocusedItem + 1; i++) {
1245 int h = GetItemHeight (i);
1250 bottom = ((FocusedItem + 1) - top_index + 1) * ItemHeight;
1253 if (bottom >= items_area.Height) {
1254 int overhang = bottom - items_area.Height;
1257 if (draw_mode == DrawMode.OwnerDrawVariable)
1258 while (overhang > 0)
1259 overhang -= (int) heights [offset];
1261 offset = (int) Math.Ceiling ((float)overhang / (float) ItemHeight);
1262 top_index += offset;
1265 selected_index = FocusedItem + 1;
1269 case ItemNavigation.Previous: {
1270 if (FocusedItem > 0) {
1271 if (FocusedItem - 1 < top_index) {
1275 selected_index = FocusedItem - 1;
1280 case ItemNavigation.NextPage: {
1281 if (Items.Count < page_size) {
1282 NavigateItemVisually (ItemNavigation.Last);
1286 if (FocusedItem + page_size - 1 >= Items.Count) {
1287 top_index = Items.Count - page_size;
1289 selected_index = Items.Count - 1;
1292 if (FocusedItem + page_size - 1 > last_visible_index) {
1293 top_index = FocusedItem;
1297 selected_index = FocusedItem + page_size - 1;
1303 case ItemNavigation.PreviousPage: {
1305 int rows = items_area.Height / ItemHeight;
1306 if (FocusedItem - (rows - 1) <= 0) {
1313 if (FocusedItem - (rows - 1) < top_index) {
1314 top_index = FocusedItem - (rows - 1);
1318 selected_index = FocusedItem - (rows - 1);
1327 return selected_index;
1331 private void OnGotFocus (object sender, EventArgs e)
1335 if (FocusedItem != -1)
1336 InvalidateItem (FocusedItem);
1339 private void OnLostFocus (object sender, EventArgs e)
1343 if (FocusedItem != -1)
1344 InvalidateItem (FocusedItem);
1347 private void OnKeyDownLB (object sender, KeyEventArgs e)
1351 if (Items.Count == 0)
1354 switch (e.KeyCode) {
1356 case Keys.ControlKey:
1357 ctrl_pressed = true;
1361 shift_pressed = true;
1365 new_item = NavigateItemVisually (ItemNavigation.First);
1369 new_item = NavigateItemVisually (ItemNavigation.Last);
1373 new_item = NavigateItemVisually (ItemNavigation.Previous);
1377 new_item = NavigateItemVisually (ItemNavigation.Next);
1381 new_item = NavigateItemVisually (ItemNavigation.PreviousPage);
1385 new_item = NavigateItemVisually (ItemNavigation.NextPage);
1389 if (multicolumn == true) {
1390 new_item = NavigateItemVisually (ItemNavigation.NextColumn);
1395 if (multicolumn == true) {
1396 new_item = NavigateItemVisually (ItemNavigation.PreviousColumn);
1401 if (selection_mode == SelectionMode.MultiSimple) {
1402 SelectedItemFromNavigation (FocusedItem);
1411 if (new_item != -1) {
1412 FocusedItem = new_item;
1413 if (selection_mode != SelectionMode.MultiSimple && selection_mode != SelectionMode.None) {
1414 SelectedItemFromNavigation (new_item);
1419 private void OnKeyUpLB (object sender, KeyEventArgs e)
1421 switch (e.KeyCode) {
1422 case Keys.ControlKey:
1423 ctrl_pressed = false;
1426 shift_pressed = false;
1433 internal void InvalidateItem (int index)
1435 Rectangle bounds = GetItemDisplayRectangle (index, top_index);
1436 if (ClientRectangle.IntersectsWith (bounds))
1437 Invalidate (bounds);
1440 internal virtual void OnItemClick (int index)
1442 OnSelectedIndexChanged (EventArgs.Empty);
1443 OnSelectedValueChanged (EventArgs.Empty);
1447 int[] prev_selection;
1448 bool button_pressed = false;
1450 private void SelectExtended (int index)
1454 ArrayList new_selection = new ArrayList ();
1455 int start = anchor < index ? anchor : index;
1456 int end = anchor > index ? anchor : index;
1457 for (int i = start; i <= end; i++)
1458 new_selection.Add (i);
1461 foreach (int i in prev_selection)
1462 if (!selection.Contains (i))
1463 new_selection.Add (i);
1465 foreach (int i in SelectedIndices)
1466 if (!new_selection.Contains (i))
1467 UnSelectItem (i, true);
1469 foreach (int i in new_selection)
1470 if (!SelectedIndices.Contains (i))
1475 private void OnMouseDownLB (object sender, MouseEventArgs e)
1477 int index = IndexAtClientPoint (e.X, e.Y);
1482 switch (SelectionMode) {
1483 case SelectionMode.One:
1484 if (SelectedIndex != index) {
1485 UnSelectItem (SelectedIndex, true);
1488 selected_index = index;
1491 case SelectionMode.MultiSimple:
1492 if (SelectedIndices.Contains (index))
1493 UnSelectItem (index, true);
1498 case SelectionMode.MultiExtended:
1499 shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0;
1500 ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0;
1503 prev_selection = new int [selection.Count];
1504 SelectedIndices.CopyTo (prev_selection, 0);
1511 SelectExtended (index);
1514 case SelectionMode.None:
1519 button_pressed = true;
1520 FocusedItem = index;
1523 private void OnMouseMoveLB (object sender, MouseEventArgs e)
1525 if (!button_pressed)
1528 int index = IndexAtClientPoint (e.X, e.Y);
1530 switch (SelectionMode) {
1531 case SelectionMode.One:
1532 if (index == selected_index)
1535 UnSelectItem (SelectedIndex, true);
1537 selected_index = index;
1540 case SelectionMode.MultiSimple:
1543 case SelectionMode.MultiExtended:
1544 SelectExtended (index);
1547 case SelectionMode.None:
1552 FocusedItem = index;
1555 private void OnMouseUpLB (object sender, MouseEventArgs e)
1558 OnDoubleClick (EventArgs.Empty);
1559 else if (e.Clicks == 1)
1560 OnClick (EventArgs.Empty);
1562 if (!button_pressed)
1565 int index = IndexAtClientPoint (e.X, e.Y);
1566 OnItemClick (index);
1567 button_pressed = ctrl_pressed = shift_pressed = false;
1570 private void Scroll (ScrollBar scrollbar, int delta)
1572 if (delta == 0 || !scrollbar.Visible || !scrollbar.Enabled)
1576 if (scrollbar == hscrollbar)
1577 max = hscrollbar.Maximum - (items_area.Width / ColumnWidthInternal) + 1;
1579 max = vscrollbar.Maximum - (items_area.Height / ItemHeight) + 1;
1581 int val = scrollbar.Value + delta;
1584 else if (val < scrollbar.Minimum)
1585 val = scrollbar.Minimum;
1586 scrollbar.Value = val;
1589 private void OnMouseWheelLB (object sender, MouseEventArgs me)
1591 if (Items.Count == 0)
1594 int lines = me.Delta / 120;
1597 Scroll (hscrollbar, -SystemInformation.MouseWheelScrollLines * lines);
1599 Scroll (vscrollbar, -lines);
1602 internal override void OnPaintInternal (PaintEventArgs pevent)
1607 Draw (pevent.ClipRectangle, pevent.Graphics);
1610 internal void RepositionScrollBars ()
1612 if (vscrollbar.is_visible) {
1613 vscrollbar.Size = new Size (vscrollbar.Width, items_area.Height);
1614 vscrollbar.Location = new Point (items_area.Width, 0);
1617 if (hscrollbar.is_visible) {
1618 hscrollbar.Size = new Size (items_area.Width, hscrollbar.Height);
1619 hscrollbar.Location = new Point (0, items_area.Height);
1623 // Add an item in the Selection array and marks it visually as selected
1624 private void SelectItem (int index)
1626 if (index == -1 || SelectedIndices.Contains (index))
1629 selection.Add (Items[index]);
1630 InvalidateItem (index);
1633 // An item navigation operation (mouse or keyboard) has caused to select a new item
1634 internal void SelectedItemFromNavigation (int index)
1636 switch (SelectionMode) {
1637 case SelectionMode.None: // Do nothing
1639 case SelectionMode.One: {
1640 SelectedIndex = index;
1643 case SelectionMode.MultiSimple: {
1644 if (SelectedIndex == -1) {
1645 SelectedIndex = index;
1648 if (SelectedIndices.Contains (index))
1649 UnSelectItem (index, true);
1652 OnSelectedIndexChanged (new EventArgs ());
1653 OnSelectedValueChanged (new EventArgs ());
1659 case SelectionMode.MultiExtended: {
1660 if (SelectedIndex == -1) {
1661 SelectedIndex = index;
1664 if (ctrl_pressed == false && shift_pressed == false) {
1668 if (shift_pressed == true) {
1669 ShiftSelection (index);
1670 } else { // ctrl_pressed or single item
1674 OnSelectedIndexChanged (new EventArgs ());
1675 OnSelectedValueChanged (new EventArgs ());
1685 private void ShiftSelection (int index)
1687 int shorter_item = -1, dist = Items.Count + 1, cur_dist;
1689 foreach (int idx in selected_indices) {
1691 cur_dist = idx - index;
1694 cur_dist = index - idx;
1697 if (cur_dist < dist) {
1703 if (shorter_item != -1) {
1706 if (shorter_item > index) {
1710 start = shorter_item;
1715 for (int idx = start; idx <= end; idx++) {
1721 internal int FocusedItem {
1722 get { return focused_item; }
1724 if (focused_item == value)
1727 int prev = focused_item;
1729 focused_item = value;
1731 if (has_focus == false)
1735 InvalidateItem (prev);
1738 InvalidateItem (value);
1742 // Removes an item in the Selection array and marks it visually as unselected
1743 private void UnSelectItem (int index, bool remove)
1749 selection.Remove (Items[index]);
1751 InvalidateItem (index);
1754 StringFormat string_format;
1755 internal StringFormat StringFormat {
1757 if (string_format == null) {
1758 string_format = new StringFormat ();
1759 if (RightToLeft == RightToLeft.Yes)
1760 string_format.Alignment = StringAlignment.Far;
1762 string_format.Alignment = StringAlignment.Near;
1764 string_format.SetTabStops (0, new float [] {(float)(Font.Height * 3.7)});
1766 return string_format;
1771 internal virtual void CollectionChanged ()
1776 if (Items.Count == 0) {
1777 selected_index = -1;
1782 if (!IsHandleCreated || suspend_layout)
1790 private void UpdateBounds ()
1792 if (requested_height == -1)
1795 SetBounds(0, 0, 0, requested_height, BoundsSpecified.Height);
1798 private void UpdateScrollBars ()
1800 items_area = ClientRectangle;
1801 if (UpdateHorizontalScrollBar ()) {
1802 items_area.Height -= hscrollbar.Height;
1803 if (UpdateVerticalScrollBar ()) {
1804 items_area.Width -= vscrollbar.Width;
1805 UpdateHorizontalScrollBar ();
1807 } else if (UpdateVerticalScrollBar ()) {
1808 items_area.Width -= vscrollbar.Width;
1809 if (UpdateHorizontalScrollBar ()) {
1810 items_area.Height -= hscrollbar.Height;
1811 UpdateVerticalScrollBar ();
1815 RepositionScrollBars ();
1818 /* Determines if the horizontal scrollbar has to be displyed */
1819 private bool UpdateHorizontalScrollBar ()
1822 bool enabled = true;
1825 if (canvas_size.Width > items_area.Width) {
1827 hscrollbar.Maximum = canvas_size.Width / ColumnWidthInternal - 1;
1828 } else if (ScrollAlwaysVisible == true) {
1831 hscrollbar.Maximum = 0;
1833 } else if (canvas_size.Width > ClientRectangle.Width && HorizontalScrollbar) {
1835 hscrollbar.Maximum = canvas_size.Width;
1836 hscrollbar.LargeChange = items_area.Width;
1839 hbar_offset = hscrollbar.Value;
1840 hscrollbar.Enabled = enabled;
1841 hscrollbar.Visible = show;
1846 /* Determines if the vertical scrollbar has to be displyed */
1847 private bool UpdateVerticalScrollBar ()
1850 vscrollbar.Visible = false;
1855 bool enabled = true;
1856 if (canvas_size.Height > items_area.Height) {
1858 vscrollbar.Maximum = Items.Count - 1;
1859 vscrollbar.LargeChange = items_area.Height / ItemHeight;
1860 } else if (ScrollAlwaysVisible) {
1863 vscrollbar.Maximum = 0;
1866 vscrollbar.Enabled = enabled;
1867 vscrollbar.Visible = show;
1873 private void VerticalScrollEvent (object sender, EventArgs e)
1875 int top_item = top_index;
1877 top_index = /*row_count + */ vscrollbar.Value;
1878 last_visible_index = LastVisibleItem ();
1880 int diff = top_item - top_index;
1882 XplatUI.ScrollWindow (Handle, items_area, 0, ItemHeight * diff, false);
1885 #endregion Private Methods
1887 [ListBindable (false)]
1888 public class ObjectCollection : IList, ICollection, IEnumerable
1890 internal class ListObjectComparer : IComparer
1892 private ListBox owner;
1894 public ListObjectComparer (ListBox owner)
1899 public int Compare (object a, object b)
1901 string str1 = a.ToString ();
1902 string str2 = b.ToString ();
1903 return str1.CompareTo (str2);
1907 private ListBox owner;
1908 internal ArrayList object_items = new ArrayList ();
1910 public ObjectCollection (ListBox owner)
1915 public ObjectCollection (ListBox owner, object[] obj)
1921 public ObjectCollection (ListBox owner, ObjectCollection obj)
1927 #region Public Properties
1929 get { return object_items.Count; }
1932 public bool IsReadOnly {
1933 get { return false; }
1937 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
1938 public virtual object this [int index] {
1940 if (index < 0 || index >= Count)
1941 throw new ArgumentOutOfRangeException ("Index of out range");
1943 return object_items[index];
1946 if (index < 0 || index >= Count)
1947 throw new ArgumentOutOfRangeException ("Index of out range");
1949 throw new ArgumentNullException ("value");
1951 object_items[index] = value;
1952 owner.CollectionChanged ();
1956 bool ICollection.IsSynchronized {
1957 get { return false; }
1960 object ICollection.SyncRoot {
1961 get { return this; }
1964 bool IList.IsFixedSize {
1965 get { return false; }
1968 #endregion Public Properties
1970 #region Public Methods
1971 public int Add (object item)
1975 idx = AddItem (item);
1976 owner.CollectionChanged ();
1980 public void AddRange (object[] items)
1982 foreach (object mi in items)
1985 owner.CollectionChanged ();
1988 public void AddRange (ObjectCollection col)
1990 foreach (object mi in col)
1993 owner.CollectionChanged ();
1996 internal void AddRange (IList list)
1998 foreach (object mi in list)
2001 owner.CollectionChanged ();
2004 public virtual void Clear ()
2006 owner.selection.Clear ();
2007 object_items.Clear ();
2008 owner.CollectionChanged ();
2010 public bool Contains (object obj)
2012 return object_items.Contains (obj);
2015 public void CopyTo (object[] dest, int arrayIndex)
2017 object_items.CopyTo (dest, arrayIndex);
2020 void ICollection.CopyTo (Array dest, int index)
2022 object_items.CopyTo (dest, index);
2025 public IEnumerator GetEnumerator ()
2027 return object_items.GetEnumerator ();
2030 int IList.Add (object item)
2035 public int IndexOf (object value)
2037 return object_items.IndexOf (value);
2040 public void Insert (int index, object item)
2042 if (index < 0 || index > Count)
2043 throw new ArgumentOutOfRangeException ("Index of out range");
2045 owner.BeginUpdate ();
2046 object_items.Insert (index, item);
2047 owner.CollectionChanged ();
2051 public void Remove (object value)
2053 RemoveAt (IndexOf (value));
2056 public void RemoveAt (int index)
2058 if (index < 0 || index >= Count)
2059 throw new ArgumentOutOfRangeException ("Index of out range");
2061 owner.selection.Remove (object_items [index]);
2062 object_items.RemoveAt (index);
2063 owner.CollectionChanged ();
2065 #endregion Public Methods
2067 #region Private Methods
2068 internal int AddItem (object item)
2071 throw new ArgumentNullException ("item");
2073 int cnt = object_items.Count;
2074 object_items.Add (item);
2078 internal void Sort ()
2080 object_items.Sort (new ListObjectComparer (owner));
2083 #endregion Private Methods
2086 public class SelectedIndexCollection : IList, ICollection, IEnumerable
2088 private ListBox owner;
2090 public SelectedIndexCollection (ListBox owner)
2095 #region Public Properties
2098 get { return owner.selection.Count; }
2101 public bool IsReadOnly {
2102 get { return true; }
2105 public int this [int index] {
2107 if (index < 0 || index >= Count)
2108 throw new ArgumentOutOfRangeException ("Index of out range");
2110 return owner.Items.IndexOf (owner.selection [index]);
2114 bool ICollection.IsSynchronized {
2115 get { return true; }
2118 bool IList.IsFixedSize{
2119 get { return true; }
2122 object ICollection.SyncRoot {
2123 get { return this; }
2126 #endregion Public Properties
2128 #region Public Methods
2129 public bool Contains (int selectedIndex)
2131 foreach (object o in owner.selection)
2132 if (owner.Items.IndexOf (o) == selectedIndex)
2137 public void CopyTo (Array dest, int index)
2139 foreach (object o in owner.selection)
2140 dest.SetValue(owner.Items.IndexOf (o), index++);
2143 public IEnumerator GetEnumerator ()
2145 //FIXME: write an enumerator that uses owner.selection.GetEnumerator
2146 // so that invalidation is write on selection changes
2147 ArrayList indices = new ArrayList ();
2148 foreach (object o in owner.selection)
2149 indices.Add (owner.Items.IndexOf (o));
2150 return indices.GetEnumerator ();
2153 int IList.Add (object obj)
2155 throw new NotSupportedException ();
2160 throw new NotSupportedException ();
2163 bool IList.Contains (object selectedIndex)
2165 return Contains ((int)selectedIndex);
2168 int IList.IndexOf (object selectedIndex)
2170 return IndexOf ((int) selectedIndex);
2173 void IList.Insert (int index, object value)
2175 throw new NotSupportedException ();
2178 void IList.Remove (object value)
2180 throw new NotSupportedException ();
2183 void IList.RemoveAt (int index)
2185 throw new NotSupportedException ();
2188 object IList.this[int index]{
2189 get {return owner.Items.IndexOf (owner.selection [index]); }
2190 set {throw new NotImplementedException (); }
2193 public int IndexOf (int selectedIndex)
2195 for (int i = 0; i < owner.selection.Count; i++)
2196 if (owner.Items.IndexOf (owner.selection [i]) == selectedIndex)
2200 #endregion Public Methods
2203 public class SelectedObjectCollection : IList, ICollection, IEnumerable
2205 private ListBox owner;
2207 public SelectedObjectCollection (ListBox owner)
2212 #region Public Properties
2214 get { return owner.selection.Count; }
2217 public bool IsReadOnly {
2218 get { return true; }
2222 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2223 public object this [int index] {
2225 if (index < 0 || index >= Count)
2226 throw new ArgumentOutOfRangeException ("Index of out range");
2228 return owner.selection [index];
2230 set {throw new NotSupportedException ();}
2233 bool ICollection.IsSynchronized {
2234 get { return true; }
2237 object ICollection.SyncRoot {
2238 get { return this; }
2241 bool IList.IsFixedSize {
2242 get { return true; }
2245 #endregion Public Properties
2247 #region Public Methods
2248 public bool Contains (object selectedObject)
2250 return owner.selection.Contains (selectedObject);
2253 public void CopyTo (Array dest, int index)
2255 owner.selection.CopyTo (dest, index);
2258 int IList.Add (object value)
2260 throw new NotSupportedException ();
2265 throw new NotSupportedException ();
2268 void IList.Insert (int index, object value)
2270 throw new NotSupportedException ();
2273 void IList.Remove (object value)
2275 throw new NotSupportedException ();
2278 void IList.RemoveAt (int index)
2280 throw new NotSupportedException ();
2283 public int IndexOf (object item)
2285 return owner.selection.IndexOf (item);
2288 public IEnumerator GetEnumerator ()
2290 return owner.selection.GetEnumerator ();
2293 #endregion Public Methods