1 /// Permission is hereby granted, free of charge, to any person obtaining 2 // a copy of this software and associated documentation files (the 3 // "Software"), to deal in the Software without restriction, including 4 // without limitation the rights to use, copy, modify, merge, publish, 5 // distribute, sublicense, and/or sell copies of the Software, and to 6 // permit persons to whom the Software is furnished to do so, subject to 7 // the following conditions: 8 // 9 // The above copyright notice and this permission notice shall be 10 // included in all copies or substantial portions of the Software. 11 // 12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 // 20 // Copyright (c) 2004-2006 Novell, Inc. 21 // 22 // Authors: 23 // Jordi Mas i Hernandez, jordi@ximian.com 24 // Mike Kestner <mkestner@novell.com> 25 // 26 27 // COMPLETE 28 29 using System; 30 using System.Drawing; 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; 39 40 namespace System.Windows.Forms 41 { 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)] 47 [ComVisible (true)] 48 public class ListBox : ListControl 49 { 50 public const int DefaultItemHeight = 13; 51 public const int NoMatches = -1; 52 53 internal enum ItemNavigation 54 { 55 First, 56 Last, 57 Next, 58 Previous, 59 NextPage, 60 PreviousPage, 61 PreviousColumn, 62 NextColumn 63 } 64 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; 96 ListBox()97 public ListBox () 98 { 99 items = CreateItemCollection (); 100 selected_indices = new SelectedIndexCollection (this); 101 selected_items = new SelectedObjectCollection (this); 102 103 requested_height = bounds.Height; 104 InternalBorderStyle = BorderStyle.Fixed3D; 105 BackColor = ThemeEngine.Current.ColorControl; 106 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; 115 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); 124 125 Controls.AddImplicit (vscrollbar); 126 Controls.AddImplicit (hscrollbar); 127 128 /* Events */ 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); 136 137 SetStyle (ControlStyles.UserPaint, false); 138 139 custom_tab_offsets = new IntegerCollection (this); 140 } 141 142 #region Events 143 static object DrawItemEvent = new object (); 144 static object MeasureItemEvent = new object (); 145 static object SelectedIndexChangedEvent = new object (); 146 147 [Browsable (false)] 148 [EditorBrowsable (EditorBrowsableState.Never)] 149 public new event EventHandler BackgroundImageChanged { 150 add { base.BackgroundImageChanged += value; } 151 remove { base.BackgroundImageChanged -= value; } 152 } 153 154 [Browsable (false)] 155 [EditorBrowsable (EditorBrowsableState.Never)] 156 public new event EventHandler BackgroundImageLayoutChanged { 157 add { base.BackgroundImageLayoutChanged += value; } 158 remove { base.BackgroundImageLayoutChanged -= value; } 159 } 160 161 [Browsable (true)] 162 [EditorBrowsable (EditorBrowsableState.Always)] 163 public new event EventHandler Click { 164 add { base.Click += value; } 165 remove { base.Click -= value; } 166 } 167 168 public event DrawItemEventHandler DrawItem { 169 add { Events.AddHandler (DrawItemEvent, value); } 170 remove { Events.RemoveHandler (DrawItemEvent, value); } 171 } 172 173 public event MeasureItemEventHandler MeasureItem { 174 add { Events.AddHandler (MeasureItemEvent, value); } 175 remove { Events.RemoveHandler (MeasureItemEvent, value); } 176 } 177 178 [Browsable (true)] 179 [EditorBrowsable (EditorBrowsableState.Always)] 180 public new event MouseEventHandler MouseClick { 181 add { base.MouseClick += value; } 182 remove { base.MouseClick -= value; } 183 } 184 185 [Browsable (false)] 186 [EditorBrowsable (EditorBrowsableState.Never)] 187 public new event EventHandler PaddingChanged { 188 add { base.PaddingChanged += value; } 189 remove { base.PaddingChanged -= value; } 190 } 191 192 [Browsable (false)] 193 [EditorBrowsable (EditorBrowsableState.Never)] 194 public new event PaintEventHandler Paint { 195 add { base.Paint += value; } 196 remove { base.Paint -= value; } 197 } 198 199 public event EventHandler SelectedIndexChanged { 200 add { Events.AddHandler (SelectedIndexChangedEvent, value); } 201 remove { Events.RemoveHandler (SelectedIndexChangedEvent, value); } 202 } 203 204 [Browsable (false)] 205 [EditorBrowsable (EditorBrowsableState.Advanced)] 206 public new event EventHandler TextChanged { 207 add { base.TextChanged += value; } 208 remove { base.TextChanged -= value; } 209 } 210 #endregion // Events 211 212 #region UIA Framework Events 213 //NOTE: 214 // We are using Reflection to add/remove internal events. 215 // Class ListProvider uses the events. 216 // 217 //Event used to generate UIA Selection Pattern 218 static object UIASelectionModeChangedEvent = new object (); 219 220 internal event EventHandler UIASelectionModeChanged { 221 add { Events.AddHandler (UIASelectionModeChangedEvent, value); } 222 remove { Events.RemoveHandler (UIASelectionModeChangedEvent, value); } 223 } 224 OnUIASelectionModeChangedEvent()225 internal void OnUIASelectionModeChangedEvent () 226 { 227 EventHandler eh = (EventHandler) Events [UIASelectionModeChangedEvent]; 228 if (eh != null) 229 eh (this, EventArgs.Empty); 230 } 231 232 static object UIAFocusedItemChangedEvent = new object (); 233 234 internal event EventHandler UIAFocusedItemChanged { 235 add { Events.AddHandler (UIAFocusedItemChangedEvent, value); } 236 remove { Events.RemoveHandler (UIAFocusedItemChangedEvent, value); } 237 } 238 OnUIAFocusedItemChangedEvent()239 internal void OnUIAFocusedItemChangedEvent () 240 { 241 EventHandler eh = (EventHandler) Events [UIAFocusedItemChangedEvent]; 242 if (eh != null) 243 eh (this, EventArgs.Empty); 244 } 245 #endregion UIA Framework Events 246 247 #region Public Properties 248 public override Color BackColor { 249 get { return base.BackColor; } 250 set { 251 if (base.BackColor == value) 252 return; 253 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 257 } 258 259 [Browsable (false)] 260 [EditorBrowsable (EditorBrowsableState.Never)] 261 public override Image BackgroundImage { 262 get { return base.BackgroundImage; } 263 set { 264 base.BackgroundImage = value; 265 base.Refresh (); 266 } 267 } 268 269 [Browsable (false)] 270 [EditorBrowsable (EditorBrowsableState.Never)] 271 public override ImageLayout BackgroundImageLayout { 272 get { return base.BackgroundImageLayout; } 273 set { base.BackgroundImageLayout = value; } 274 } 275 276 [DefaultValue (BorderStyle.Fixed3D)] 277 [DispId(-504)] 278 public BorderStyle BorderStyle { 279 get { return InternalBorderStyle; } 280 set { 281 InternalBorderStyle = value; 282 UpdateListBoxBounds (); 283 } 284 } 285 286 [DefaultValue (0)] 287 [Localizable (true)] 288 public int ColumnWidth { 289 get { return column_width; } 290 set { 291 if (value < 0) 292 throw new ArgumentException ("A value less than zero is assigned to the property."); 293 294 column_width = value; 295 296 if (value == 0) 297 ColumnWidthInternal = 120; 298 else 299 ColumnWidthInternal = value; 300 301 base.Refresh (); 302 } 303 } 304 305 protected override CreateParams CreateParams { 306 get { return base.CreateParams;} 307 } 308 309 [Browsable (false)] 310 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)] 311 public IntegerCollection CustomTabOffsets { 312 get { return custom_tab_offsets; } 313 } 314 315 protected override Size DefaultSize { 316 get { return new Size (120, 96); } 317 } 318 319 [RefreshProperties(RefreshProperties.Repaint)] 320 [DefaultValue (DrawMode.Normal)] 321 public virtual DrawMode DrawMode { 322 get { return draw_mode; } 323 set { 324 if (!Enum.IsDefined (typeof (DrawMode), value)) 325 throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for DrawMode", value)); 326 327 if (value == DrawMode.OwnerDrawVariable && multicolumn == true) 328 throw new ArgumentException ("Cannot have variable height and multicolumn"); 329 330 if (draw_mode == value) 331 return; 332 333 draw_mode = value; 334 335 if (draw_mode == DrawMode.OwnerDrawVariable) 336 item_heights = new Hashtable (); 337 else 338 item_heights = null; 339 340 if (Parent != null) 341 Parent.PerformLayout (this, "DrawMode"); 342 base.Refresh (); 343 } 344 } 345 346 public override Font Font { 347 get { return base.Font; } 348 set { base.Font = value; } 349 } 350 351 public override Color ForeColor { 352 get { return base.ForeColor; } 353 set { 354 if (base.ForeColor == value) 355 return; 356 357 base.ForeColor = value; 358 base.Refresh (); 359 } 360 } 361 362 [DefaultValue (0)] 363 [Localizable (true)] 364 public int HorizontalExtent { 365 get { return horizontal_extent; } 366 set { 367 if (horizontal_extent == value) 368 return; 369 370 horizontal_extent = value; 371 base.Refresh (); 372 } 373 } 374 375 [DefaultValue (false)] 376 [Localizable (true)] 377 public bool HorizontalScrollbar { 378 get { return horizontal_scrollbar; } 379 set { 380 if (horizontal_scrollbar == value) 381 return; 382 383 horizontal_scrollbar = value; 384 UpdateScrollBars (); 385 base.Refresh (); 386 } 387 } 388 389 [DefaultValue (true)] 390 [Localizable (true)] 391 [RefreshProperties(RefreshProperties.Repaint)] 392 public bool IntegralHeight { 393 get { return integral_height; } 394 set { 395 if (integral_height == value) 396 return; 397 398 integral_height = value; 399 UpdateListBoxBounds (); 400 } 401 } 402 403 [DefaultValue (13)] 404 [Localizable (true)] 405 [RefreshProperties(RefreshProperties.Repaint)] 406 public virtual int ItemHeight { 407 get { 408 if (item_height == -1) { 409 SizeF sz = TextRenderer.MeasureString ("The quick brown Fox", Font); 410 item_height = (int) sz.Height; 411 } 412 return item_height; 413 } 414 set { 415 if (value > 255) 416 throw new ArgumentOutOfRangeException ("The ItemHeight property was set beyond 255 pixels"); 417 418 explicit_item_height = true; 419 if (item_height == value) 420 return; 421 422 item_height = value; 423 if (IntegralHeight) 424 UpdateListBoxBounds (); 425 LayoutListBox (); 426 } 427 } 428 429 [DesignerSerializationVisibility (DesignerSerializationVisibility.Content)] 430 [Localizable (true)] 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; } 435 } 436 437 [DefaultValue (false)] 438 public bool MultiColumn { 439 get { return multicolumn; } 440 set { 441 if (multicolumn == value) 442 return; 443 444 if (value == true && DrawMode == DrawMode.OwnerDrawVariable) 445 throw new ArgumentException ("A multicolumn ListBox cannot have a variable-sized height."); 446 447 multicolumn = value; 448 LayoutListBox (); 449 Invalidate (); 450 } 451 } 452 453 [Browsable (false)] 454 [EditorBrowsable (EditorBrowsableState.Never)] 455 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] 456 public new Padding Padding { 457 get { return padding; } 458 set { padding = value; } 459 } 460 461 [Browsable (false)] 462 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] 463 [EditorBrowsable (EditorBrowsableState.Advanced)] 464 public int PreferredHeight { 465 get { 466 int itemsHeight = 0; 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]]; 474 } 475 476 return itemsHeight; 477 } 478 } 479 480 public override RightToLeft RightToLeft { 481 get { return base.RightToLeft; } 482 set { 483 base.RightToLeft = value; 484 if (base.RightToLeft == RightToLeft.Yes) 485 StringFormat.Alignment = StringAlignment.Far; 486 else 487 StringFormat.Alignment = StringAlignment.Near; 488 base.Refresh (); 489 } 490 } 491 492 // Only affects the Vertical ScrollBar 493 [DefaultValue (false)] 494 [Localizable (true)] 495 public bool ScrollAlwaysVisible { 496 get { return scroll_always_visible; } 497 set { 498 if (scroll_always_visible == value) 499 return; 500 501 scroll_always_visible = value; 502 UpdateScrollBars (); 503 } 504 } 505 506 [Bindable(true)] 507 [Browsable (false)] 508 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] 509 public override int SelectedIndex { 510 get { 511 if (selected_indices == null) 512 return -1; 513 514 return selected_indices.Count > 0 ? selected_indices [0] : -1; 515 } 516 set { 517 if (value < -1 || value >= Items.Count) 518 throw new ArgumentOutOfRangeException ("Index of out range"); 519 520 if (SelectionMode == SelectionMode.None) 521 throw new ArgumentException ("cannot call this method if SelectionMode is SelectionMode.None"); 522 523 if (value == -1) 524 selected_indices.Clear (); 525 else 526 selected_indices.Add (value); 527 } 528 } 529 530 [Browsable (false)] 531 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] 532 public SelectedIndexCollection SelectedIndices { 533 get { return selected_indices; } 534 } 535 536 [Bindable(true)] 537 [Browsable (false)] 538 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] 539 public object SelectedItem { 540 get { 541 if (SelectedItems.Count > 0) 542 return SelectedItems[0]; 543 else 544 return null; 545 } 546 set { 547 if (value != null && !Items.Contains (value)) 548 return; // FIXME: this is probably an exception 549 550 SelectedIndex = value == null ? - 1 : Items.IndexOf (value); 551 } 552 } 553 554 [Browsable (false)] 555 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] 556 public SelectedObjectCollection SelectedItems { 557 get {return selected_items;} 558 } 559 560 [DefaultValue (SelectionMode.One)] 561 public virtual SelectionMode SelectionMode { 562 get { return selection_mode; } 563 set { 564 if (!Enum.IsDefined (typeof (SelectionMode), value)) 565 throw new InvalidEnumArgumentException (string.Format("Enum argument value '{0}' is not valid for SelectionMode", value)); 566 567 if (selection_mode == value) 568 return; 569 570 selection_mode = value; 571 572 switch (selection_mode) { 573 case SelectionMode.None: 574 SelectedIndices.Clear (); 575 break; 576 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]); 582 break; 583 584 default: 585 break; 586 } 587 588 // UIA Framework: Generates SelectionModeChanged event. 589 OnUIASelectionModeChangedEvent (); 590 } 591 } 592 593 [DefaultValue (false)] 594 public bool Sorted { 595 get { return sorted; } 596 set { 597 if (sorted == value) 598 return; 599 600 sorted = value; 601 if (sorted) 602 Sort (); 603 } 604 } 605 606 [Bindable (false)] 607 [Browsable (false)] 608 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] 609 [EditorBrowsable (EditorBrowsableState.Advanced)] 610 public override string Text { 611 get { 612 if (SelectionMode != SelectionMode.None && SelectedIndex != -1) 613 return GetItemText (SelectedItem); 614 615 return base.Text; 616 } 617 set { 618 619 base.Text = value; 620 621 if (SelectionMode == SelectionMode.None) 622 return; 623 624 int index; 625 626 index = FindStringExact (value); 627 628 if (index == -1) 629 return; 630 631 SelectedIndex = index; 632 } 633 } 634 635 [Browsable (false)] 636 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)] 637 public int TopIndex { 638 get { return top_index; } 639 set { 640 if (value == top_index) 641 return; 642 643 if (value < 0 || value >= Items.Count) 644 return; 645 646 int page_size = (items_area.Height / ItemHeight); 647 648 if (Items.Count < page_size) 649 value = 0; 650 else if (!multicolumn) 651 top_index = Math.Min (value, Items.Count - page_size); 652 else 653 top_index = value; 654 655 UpdateTopItem (); 656 base.Refresh (); 657 } 658 } 659 660 [Browsable (false)] 661 [DefaultValue (false)] 662 public bool UseCustomTabOffsets { 663 get { return use_custom_tab_offsets; } 664 set { 665 if (use_custom_tab_offsets != value) { 666 use_custom_tab_offsets = value; 667 CalculateTabStops (); 668 } 669 } 670 } 671 672 [DefaultValue (true)] 673 public bool UseTabStops { 674 get { return use_tabstops; } 675 set { 676 if (use_tabstops == value) 677 return; 678 679 use_tabstops = value; 680 CalculateTabStops (); 681 } 682 } 683 684 protected override bool AllowSelection { 685 get { 686 return SelectionMode != SelectionMode.None; 687 } 688 } 689 #endregion Public Properties 690 691 #region Private Properties 692 693 private int ColumnWidthInternal { 694 get { return column_width_internal; } 695 set { column_width_internal = value; } 696 } 697 698 private int row_count = 1; 699 private int RowCount { 700 get { 701 return MultiColumn ? row_count : Items.Count; 702 } 703 } 704 705 #endregion Private Properties 706 707 #region UIA Framework Properties 708 709 internal ScrollBar UIAHScrollBar { 710 get { return hscrollbar; } 711 } 712 713 internal ScrollBar UIAVScrollBar { 714 get { return vscrollbar; } 715 } 716 717 #endregion UIA Framework Properties 718 719 #region Public Methods 720 [Obsolete ("this method has been deprecated")] AddItemsCore(object[] value)721 protected virtual void AddItemsCore (object[] value) 722 { 723 Items.AddRange (value); 724 } 725 BeginUpdate()726 public void BeginUpdate () 727 { 728 suspend_layout = true; 729 } 730 ClearSelected()731 public void ClearSelected () 732 { 733 selected_indices.Clear (); 734 } 735 CreateItemCollection()736 protected virtual ObjectCollection CreateItemCollection () 737 { 738 return new ObjectCollection (this); 739 } 740 EndUpdate()741 public void EndUpdate () 742 { 743 suspend_layout = false; 744 LayoutListBox (); 745 base.Refresh (); 746 } 747 FindString(String s)748 public int FindString (String s) 749 { 750 return FindString (s, -1); 751 } 752 FindString(string s, int startIndex)753 public int FindString (string s, int startIndex) 754 { 755 if (Items.Count == 0) 756 return -1; // No exception throwing if empty 757 758 if (startIndex < -1 || startIndex >= Items.Count) 759 throw new ArgumentOutOfRangeException ("Index of out range"); 760 761 startIndex = (startIndex == Items.Count - 1) ? 0 : startIndex + 1; 762 763 int i = startIndex; 764 while (true) { 765 string text = GetItemText (Items [i]); 766 if (CultureInfo.CurrentCulture.CompareInfo.IsPrefix (text, s, 767 CompareOptions.IgnoreCase)) 768 return i; 769 770 i = (i == Items.Count - 1) ? 0 : i + 1; 771 if (i == startIndex) 772 break; 773 } 774 775 return NoMatches; 776 } 777 FindStringExact(string s)778 public int FindStringExact (string s) 779 { 780 return FindStringExact (s, -1); 781 } 782 FindStringExact(string s, int startIndex)783 public int FindStringExact (string s, int startIndex) 784 { 785 if (Items.Count == 0) 786 return -1; // No exception throwing if empty 787 788 if (startIndex < -1 || startIndex >= Items.Count) 789 throw new ArgumentOutOfRangeException ("Index of out range"); 790 791 startIndex = (startIndex + 1 == Items.Count) ? 0 : startIndex + 1; 792 793 int i = startIndex; 794 while (true) { 795 if (String.Compare (GetItemText (Items[i]), s, true) == 0) 796 return i; 797 798 i = (i + 1 == Items.Count) ? 0 : i + 1; 799 if (i == startIndex) 800 break; 801 } 802 803 return NoMatches; 804 } 805 GetItemHeight(int index)806 public int GetItemHeight (int index) 807 { 808 if (index < 0 || index >= Items.Count) 809 throw new ArgumentOutOfRangeException ("Index of out range"); 810 811 if (DrawMode == DrawMode.OwnerDrawVariable && IsHandleCreated == true) { 812 813 object o = Items [index]; 814 if (item_heights.Contains (o)) 815 return (int) item_heights [o]; 816 817 MeasureItemEventArgs args = new MeasureItemEventArgs (DeviceContext, index, ItemHeight); 818 OnMeasureItem (args); 819 item_heights [o] = args.ItemHeight; 820 return args.ItemHeight; 821 } 822 823 return ItemHeight; 824 } 825 GetItemRectangle(int index)826 public Rectangle GetItemRectangle (int index) 827 { 828 if (index < 0 || index >= Items.Count) 829 throw new ArgumentOutOfRangeException ("GetItemRectangle index out of range."); 830 831 Rectangle rect = new Rectangle (); 832 833 if (MultiColumn) { 834 int col = index / RowCount; 835 int y = index; 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; 842 } else { 843 rect.X = 0; 844 rect.Height = GetItemHeight (index); 845 rect.Width = items_area.Width; 846 847 if (DrawMode == DrawMode.OwnerDrawVariable) { 848 rect.Y = 0; 849 if (index >= top_index) { 850 for (int i = top_index; i < index; i++) { 851 rect.Y += GetItemHeight (i); 852 } 853 } else { 854 for (int i = index; i < top_index; i++) { 855 rect.Y -= GetItemHeight (i); 856 } 857 } 858 } else { 859 rect.Y = ItemHeight * (index - top_index); 860 } 861 } 862 863 if (this is CheckedListBox) 864 rect.Width += 15; 865 866 return rect; 867 } 868 869 [EditorBrowsable (EditorBrowsableState.Advanced)] GetScaledBounds(Rectangle bounds, SizeF factor, BoundsSpecified specified)870 protected override Rectangle GetScaledBounds (Rectangle bounds, SizeF factor, BoundsSpecified specified) 871 { 872 bounds.Height = requested_height; 873 874 return base.GetScaledBounds (bounds, factor, specified); 875 } 876 GetSelected(int index)877 public bool GetSelected (int index) 878 { 879 if (index < 0 || index >= Items.Count) 880 throw new ArgumentOutOfRangeException ("Index of out range"); 881 882 return SelectedIndices.Contains (index); 883 } 884 IndexFromPoint(Point p)885 public int IndexFromPoint (Point p) 886 { 887 return IndexFromPoint (p.X, p.Y); 888 } 889 890 // Only returns visible points IndexFromPoint(int x, int y)891 public int IndexFromPoint (int x, int y) 892 { 893 894 if (Items.Count == 0) { 895 return -1; 896 } 897 898 for (int i = top_index; i <= last_visible_index; i++) { 899 if (GetItemRectangle (i).Contains (x,y) == true) 900 return i; 901 } 902 903 return -1; 904 } 905 OnChangeUICues(UICuesEventArgs e)906 protected override void OnChangeUICues (UICuesEventArgs e) 907 { 908 base.OnChangeUICues (e); 909 } 910 OnDataSourceChanged(EventArgs e)911 protected override void OnDataSourceChanged (EventArgs e) 912 { 913 base.OnDataSourceChanged (e); 914 BindDataItems (); 915 916 if (DataSource == null || DataManager == null) { 917 SelectedIndex = -1; 918 } else { 919 SelectedIndex = DataManager.Position; 920 } 921 } 922 OnDisplayMemberChanged(EventArgs e)923 protected override void OnDisplayMemberChanged (EventArgs e) 924 { 925 base.OnDisplayMemberChanged (e); 926 927 if (DataManager == null || !IsHandleCreated) 928 return; 929 930 BindDataItems (); 931 base.Refresh (); 932 } 933 OnDrawItem(DrawItemEventArgs e)934 protected virtual void OnDrawItem (DrawItemEventArgs e) 935 { 936 switch (DrawMode) { 937 case DrawMode.OwnerDrawFixed: 938 case DrawMode.OwnerDrawVariable: 939 DrawItemEventHandler eh = (DrawItemEventHandler)(Events [DrawItemEvent]); 940 if (eh != null) 941 eh (this, e); 942 943 break; 944 945 default: 946 ThemeEngine.Current.DrawListBoxItem (this, e); 947 break; 948 } 949 } 950 OnFontChanged(EventArgs e)951 protected override void OnFontChanged (EventArgs e) 952 { 953 base.OnFontChanged (e); 954 955 if (use_tabstops) 956 StringFormat.SetTabStops (0, new float [] {(float)(Font.Height * 3.7)}); 957 958 if (explicit_item_height) { 959 base.Refresh (); 960 } else { 961 SizeF sz = TextRenderer.MeasureString ("The quick brown Fox", Font); 962 item_height = (int) sz.Height; 963 if (IntegralHeight) 964 UpdateListBoxBounds (); 965 LayoutListBox (); 966 } 967 } 968 OnHandleCreated(EventArgs e)969 protected override void OnHandleCreated (EventArgs e) 970 { 971 base.OnHandleCreated (e); 972 973 if (IntegralHeight) 974 UpdateListBoxBounds (); 975 976 LayoutListBox (); 977 EnsureVisible (focused_item); 978 } 979 OnHandleDestroyed(EventArgs e)980 protected override void OnHandleDestroyed (EventArgs e) 981 { 982 base.OnHandleDestroyed (e); 983 } 984 OnMeasureItem(MeasureItemEventArgs e)985 protected virtual void OnMeasureItem (MeasureItemEventArgs e) 986 { 987 if (draw_mode != DrawMode.OwnerDrawVariable) 988 return; 989 990 MeasureItemEventHandler eh = (MeasureItemEventHandler)(Events [MeasureItemEvent]); 991 if (eh != null) 992 eh (this, e); 993 } 994 OnParentChanged(EventArgs e)995 protected override void OnParentChanged (EventArgs e) 996 { 997 base.OnParentChanged (e); 998 } 999 OnResize(EventArgs e)1000 protected override void OnResize (EventArgs e) 1001 { 1002 base.OnResize (e); 1003 if (canvas_size.IsEmpty || MultiColumn) 1004 LayoutListBox (); 1005 1006 Invalidate (); 1007 } 1008 OnSelectedIndexChanged(EventArgs e)1009 protected override void OnSelectedIndexChanged (EventArgs e) 1010 { 1011 base.OnSelectedIndexChanged (e); 1012 1013 EventHandler eh = (EventHandler)(Events [SelectedIndexChangedEvent]); 1014 if (eh != null) 1015 eh (this, e); 1016 } 1017 OnSelectedValueChanged(EventArgs e)1018 protected override void OnSelectedValueChanged (EventArgs e) 1019 { 1020 base.OnSelectedValueChanged (e); 1021 } 1022 Refresh()1023 public override void Refresh () 1024 { 1025 if (draw_mode == DrawMode.OwnerDrawVariable) 1026 item_heights.Clear (); 1027 1028 base.Refresh (); 1029 } 1030 RefreshItem(int index)1031 protected override void RefreshItem (int index) 1032 { 1033 if (index < 0 || index >= Items.Count) 1034 throw new ArgumentOutOfRangeException ("Index of out range"); 1035 1036 if (draw_mode == DrawMode.OwnerDrawVariable) 1037 item_heights.Remove (Items [index]); 1038 } 1039 RefreshItems()1040 protected override void RefreshItems () 1041 { 1042 for (int i = 0; i < Items.Count; i++) { 1043 RefreshItem (i); 1044 } 1045 1046 LayoutListBox (); 1047 Refresh (); 1048 } 1049 ResetBackColor()1050 public override void ResetBackColor () 1051 { 1052 base.ResetBackColor (); 1053 } 1054 ResetForeColor()1055 public override void ResetForeColor () 1056 { 1057 base.ResetForeColor (); 1058 } 1059 ScaleControl(SizeF factor, BoundsSpecified specified)1060 protected override void ScaleControl (SizeF factor, BoundsSpecified specified) 1061 { 1062 base.ScaleControl (factor, specified); 1063 } 1064 SnapHeightToIntegral(int height)1065 private int SnapHeightToIntegral (int height) 1066 { 1067 int border; 1068 1069 switch (border_style) { 1070 case BorderStyle.Fixed3D: 1071 border = ThemeEngine.Current.Border3DSize.Height; 1072 break; 1073 case BorderStyle.FixedSingle: 1074 border = ThemeEngine.Current.BorderSize.Height; 1075 break; 1076 case BorderStyle.None: 1077 default: 1078 border = 0; 1079 break; 1080 } 1081 1082 height -= (2 * border); 1083 height -= height % ItemHeight; 1084 height += (2 * border); 1085 1086 return height; 1087 } 1088 SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified)1089 protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified) 1090 { 1091 if ((specified & BoundsSpecified.Height) == BoundsSpecified.Height) 1092 requested_height = height; 1093 1094 if (IntegralHeight && IsHandleCreated) 1095 height = SnapHeightToIntegral (height); 1096 1097 base.SetBoundsCore (x, y, width, height, specified); 1098 UpdateScrollBars (); 1099 last_visible_index = LastVisibleItem (); 1100 } 1101 SetItemCore(int index, object value)1102 protected override void SetItemCore (int index, object value) 1103 { 1104 if (index < 0 || index >= Items.Count) 1105 return; 1106 1107 Items[index] = value; 1108 } 1109 SetItemsCore(IList value)1110 protected override void SetItemsCore (IList value) 1111 { 1112 BeginUpdate (); 1113 try { 1114 Items.Clear (); 1115 Items.AddItems (value); 1116 } finally { 1117 EndUpdate (); 1118 } 1119 } 1120 SetSelected(int index, bool value)1121 public void SetSelected (int index, bool value) 1122 { 1123 if (index < 0 || index >= Items.Count) 1124 throw new ArgumentOutOfRangeException ("Index of out range"); 1125 1126 if (SelectionMode == SelectionMode.None) 1127 throw new InvalidOperationException (); 1128 1129 if (value) 1130 SelectedIndices.Add (index); 1131 else 1132 SelectedIndices.Remove (index); 1133 } 1134 Sort()1135 protected virtual void Sort () 1136 { 1137 Sort (true); 1138 } 1139 1140 // 1141 // Sometimes we could need to Sort, and request a Refresh 1142 // in a different place, to not have the painting done twice 1143 // Sort(bool paint)1144 void Sort (bool paint) 1145 { 1146 if (Items.Count == 0) 1147 return; 1148 1149 Items.Sort (); 1150 1151 if (paint) 1152 base.Refresh (); 1153 } 1154 ToString()1155 public override string ToString () 1156 { 1157 return base.ToString (); 1158 } 1159 WmReflectCommand(ref Message m)1160 protected virtual void WmReflectCommand (ref Message m) 1161 { 1162 } 1163 WndProc(ref Message m)1164 protected override void WndProc (ref Message m) 1165 { 1166 if ((Msg)m.Msg == Msg.WM_KEYDOWN) { 1167 if (ProcessKeyMessage (ref m)) 1168 m.Result = IntPtr.Zero; 1169 else { 1170 HandleKeyDown ((Keys)m.WParam.ToInt32 ()); 1171 DefWndProc (ref m); 1172 } 1173 1174 return; 1175 } 1176 1177 base.WndProc (ref m); 1178 } 1179 1180 #endregion Public Methods 1181 1182 #region Private Methods 1183 CalculateTabStops()1184 private void CalculateTabStops () 1185 { 1186 if (use_tabstops) { 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); 1191 } 1192 else 1193 StringFormat.SetTabStops (0, new float[] { (float)(Font.Height * 3.7) }); 1194 } else 1195 StringFormat.SetTabStops (0, new float[0]); 1196 1197 this.Invalidate (); 1198 } 1199 1200 private Size canvas_size; 1201 LayoutListBox()1202 private void LayoutListBox () 1203 { 1204 if (!IsHandleCreated || suspend_layout) 1205 return; 1206 1207 if (MultiColumn) 1208 LayoutMultiColumn (); 1209 else 1210 LayoutSingleColumn (); 1211 1212 last_visible_index = LastVisibleItem (); 1213 UpdateScrollBars (); 1214 } 1215 LayoutSingleColumn()1216 private void LayoutSingleColumn () 1217 { 1218 int height, width; 1219 1220 switch (DrawMode) { 1221 case DrawMode.OwnerDrawVariable: 1222 height = 0; 1223 width = HorizontalExtent; 1224 for (int i = 0; i < Items.Count; i++) { 1225 height += GetItemHeight (i); 1226 } 1227 break; 1228 1229 case DrawMode.OwnerDrawFixed: 1230 height = Items.Count * ItemHeight; 1231 width = HorizontalExtent; 1232 break; 1233 1234 case DrawMode.Normal: 1235 default: 1236 height = Items.Count * ItemHeight; 1237 width = 0; 1238 for (int i = 0; i < Items.Count; i++) { 1239 SizeF sz = TextRenderer.MeasureString (GetItemText (Items[i]), Font); 1240 int t = (int)sz.Width; 1241 1242 if (this is CheckedListBox) 1243 t += 15; 1244 1245 if (t > width) 1246 width = t; 1247 } 1248 break; 1249 } 1250 1251 canvas_size = new Size (width, height); 1252 } 1253 LayoutMultiColumn()1254 private void LayoutMultiColumn () 1255 { 1256 int usable_height = ClientRectangle.Height - (ScrollAlwaysVisible ? hscrollbar.Height : 0); 1257 row_count = Math.Max (1, usable_height / ItemHeight); 1258 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); 1266 } 1267 canvas_size = sz; 1268 } 1269 Draw(Rectangle clip, Graphics dc)1270 internal void Draw (Rectangle clip, Graphics dc) 1271 { 1272 Theme theme = ThemeEngine.Current; 1273 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); 1279 } 1280 1281 dc.FillRectangle (theme.ResPool.GetSolidBrush (BackColor), items_area); 1282 1283 if (Items.Count == 0) 1284 return; 1285 1286 for (int i = top_index; i <= last_visible_index; i++) { 1287 Rectangle rect = GetItemDisplayRectangle (i, top_index); 1288 1289 if (!clip.IntersectsWith (rect)) 1290 continue; 1291 1292 DrawItemState state = DrawItemState.None; 1293 1294 if (SelectedIndices.Contains (i)) 1295 state |= DrawItemState.Selected; 1296 1297 if (has_focus && FocusedItem == i) 1298 state |= DrawItemState.Focus; 1299 1300 if (MultiColumn == false && hscrollbar != null && hscrollbar.Visible) { 1301 rect.X -= hscrollbar.Value; 1302 rect.Width += hscrollbar.Value; 1303 } 1304 1305 Color fore_color = !Enabled ? ThemeEngine.Current.ColorGrayText : 1306 (state & DrawItemState.Selected) != 0 ? ThemeEngine.Current.ColorHighlightText : ForeColor; 1307 OnDrawItem (new DrawItemEventArgs (dc, Font, rect, i, state, fore_color, BackColor)); 1308 } 1309 } 1310 1311 // Converts a GetItemRectangle to a one that we can display GetItemDisplayRectangle(int index, int first_displayble)1312 internal Rectangle GetItemDisplayRectangle (int index, int first_displayble) 1313 { 1314 Rectangle item_rect; 1315 Rectangle first_item_rect = GetItemRectangle (first_displayble); 1316 item_rect = GetItemRectangle (index); 1317 item_rect.X -= first_item_rect.X; 1318 item_rect.Y -= first_item_rect.Y; 1319 1320 // Subtract the checkboxes from the width 1321 if (this is CheckedListBox) 1322 item_rect.Width -= 14; 1323 1324 return item_rect; 1325 } 1326 1327 // Value Changed HorizontalScrollEvent(object sender, EventArgs e)1328 private void HorizontalScrollEvent (object sender, EventArgs e) 1329 { 1330 if (multicolumn) { 1331 int top_item = top_index; 1332 int last_item = last_visible_index; 1333 1334 top_index = RowCount * hscrollbar.Value; 1335 last_visible_index = LastVisibleItem (); 1336 1337 if (top_item != top_index || last_item != last_visible_index) 1338 Invalidate (items_area); 1339 } 1340 else { 1341 int old_offset = hbar_offset; 1342 hbar_offset = hscrollbar.Value; 1343 1344 if (hbar_offset < 0) 1345 hbar_offset = 0; 1346 1347 if (IsHandleCreated) { 1348 XplatUI.ScrollWindow (Handle, items_area, old_offset - hbar_offset, 0, false); 1349 1350 // Invalidate the previous selection border, to keep it properly updated. 1351 Rectangle selection_border_area = new Rectangle (items_area.Width - (hbar_offset - old_offset) - 3, 0, 1352 3, items_area.Height); 1353 Invalidate (selection_border_area); 1354 } 1355 } 1356 } 1357 1358 // Only returns visible points. The diference of with IndexFromPoint is that the rectangle 1359 // has screen coordinates IndexAtClientPoint(int x, int y)1360 private int IndexAtClientPoint (int x, int y) 1361 { 1362 if (Items.Count == 0) 1363 return -1; 1364 1365 if (x < 0) 1366 x = 0; 1367 else if (x > ClientRectangle.Right) 1368 x = ClientRectangle.Right; 1369 1370 if (y < 0) 1371 y = 0; 1372 else if (y > ClientRectangle.Bottom) 1373 y = ClientRectangle.Bottom; 1374 1375 for (int i = top_index; i <= last_visible_index; i++) 1376 if (GetItemDisplayRectangle (i, top_index).Contains (x, y)) 1377 return i; 1378 1379 return -1; 1380 } 1381 IsInputCharInternal(char charCode)1382 internal override bool IsInputCharInternal (char charCode) 1383 { 1384 return true; 1385 } 1386 LastVisibleItem()1387 private int LastVisibleItem () 1388 { 1389 Rectangle item_rect; 1390 int top_y = items_area.Y + items_area.Height; 1391 int i = 0; 1392 1393 if (top_index >= Items.Count) 1394 return top_index; 1395 1396 for (i = top_index; i < Items.Count; i++) { 1397 item_rect = GetItemDisplayRectangle (i, top_index); 1398 if (MultiColumn) { 1399 if (item_rect.X > items_area.Width) 1400 return i - 1; 1401 } else { 1402 if (item_rect.Y + item_rect.Height > top_y) 1403 return i; 1404 } 1405 } 1406 return i - 1; 1407 } 1408 UpdateTopItem()1409 private void UpdateTopItem () 1410 { 1411 if (MultiColumn) { 1412 int col = top_index / RowCount; 1413 1414 if (col > hscrollbar.Maximum) 1415 hscrollbar.Value = hscrollbar.Maximum; 1416 else 1417 hscrollbar.Value = col; 1418 } else { 1419 if (top_index > vscrollbar.Maximum) 1420 vscrollbar.Value = vscrollbar.Maximum; 1421 else 1422 vscrollbar.Value = top_index; 1423 Scroll (vscrollbar, vscrollbar.Value - top_index); 1424 } 1425 } 1426 1427 // Navigates to the indicated item and returns the new item NavigateItemVisually(ItemNavigation navigation)1428 private int NavigateItemVisually (ItemNavigation navigation) 1429 { 1430 int page_size, columns, selected_index = -1; 1431 1432 if (multicolumn) { 1433 columns = items_area.Width / ColumnWidthInternal; 1434 page_size = columns * RowCount; 1435 if (page_size == 0) { 1436 page_size = RowCount; 1437 } 1438 } else { 1439 page_size = items_area.Height / ItemHeight; 1440 } 1441 1442 switch (navigation) { 1443 1444 case ItemNavigation.PreviousColumn: { 1445 if (SelectedIndex - RowCount < 0) { 1446 return -1; 1447 } 1448 1449 if (SelectedIndex - RowCount < top_index) { 1450 top_index = SelectedIndex - RowCount; 1451 UpdateTopItem (); 1452 } 1453 1454 selected_index = SelectedIndex - RowCount; 1455 break; 1456 } 1457 1458 case ItemNavigation.NextColumn: { 1459 if (SelectedIndex + RowCount >= Items.Count) { 1460 break; 1461 } 1462 1463 if (SelectedIndex + RowCount > last_visible_index) { 1464 top_index = SelectedIndex; 1465 UpdateTopItem (); 1466 } 1467 1468 selected_index = SelectedIndex + RowCount; 1469 break; 1470 } 1471 1472 case ItemNavigation.First: { 1473 top_index = 0; 1474 selected_index = 0; 1475 UpdateTopItem (); 1476 break; 1477 } 1478 1479 case ItemNavigation.Last: { 1480 1481 int rows = items_area.Height / ItemHeight; 1482 1483 if (multicolumn) { 1484 selected_index = Items.Count - 1; 1485 break; 1486 } 1487 if (Items.Count < rows) { 1488 top_index = 0; 1489 selected_index = Items.Count - 1; 1490 UpdateTopItem (); 1491 } else { 1492 top_index = Items.Count - rows; 1493 selected_index = Items.Count - 1; 1494 UpdateTopItem (); 1495 } 1496 break; 1497 } 1498 1499 case ItemNavigation.Next: { 1500 if (FocusedItem == Items.Count - 1) 1501 return -1; 1502 1503 if (multicolumn) { 1504 selected_index = FocusedItem + 1; 1505 break; 1506 } 1507 1508 int bottom = 0; 1509 ArrayList heights = new ArrayList (); 1510 if (draw_mode == DrawMode.OwnerDrawVariable) { 1511 for (int i = top_index; i <= FocusedItem + 1; i++) { 1512 int h = GetItemHeight (i); 1513 bottom += h; 1514 heights.Add (h); 1515 } 1516 } else { 1517 bottom = ((FocusedItem + 1) - top_index + 1) * ItemHeight; 1518 } 1519 1520 if (bottom >= items_area.Height) { 1521 int overhang = bottom - items_area.Height; 1522 1523 int offset = 0; 1524 if (draw_mode == DrawMode.OwnerDrawVariable) 1525 while (overhang > 0) 1526 overhang -= (int) heights [offset]; 1527 else 1528 offset = (int) Math.Ceiling ((float)overhang / (float) ItemHeight); 1529 top_index += offset; 1530 UpdateTopItem (); 1531 } 1532 selected_index = FocusedItem + 1; 1533 break; 1534 } 1535 1536 case ItemNavigation.Previous: { 1537 if (FocusedItem > 0) { 1538 if (FocusedItem - 1 < top_index) { 1539 top_index--; 1540 UpdateTopItem (); 1541 } 1542 selected_index = FocusedItem - 1; 1543 } 1544 break; 1545 } 1546 1547 case ItemNavigation.NextPage: { 1548 if (Items.Count < page_size) { 1549 NavigateItemVisually (ItemNavigation.Last); 1550 break; 1551 } 1552 1553 if (FocusedItem + page_size - 1 >= Items.Count) { 1554 top_index = Items.Count - page_size; 1555 UpdateTopItem (); 1556 selected_index = Items.Count - 1; 1557 } 1558 else { 1559 if (FocusedItem + page_size - 1 > last_visible_index) { 1560 top_index = FocusedItem; 1561 UpdateTopItem (); 1562 } 1563 1564 selected_index = FocusedItem + page_size - 1; 1565 } 1566 1567 break; 1568 } 1569 1570 case ItemNavigation.PreviousPage: { 1571 1572 int rows = items_area.Height / ItemHeight; 1573 if (FocusedItem - (rows - 1) <= 0) { 1574 top_index = 0; 1575 UpdateTopItem (); 1576 selected_index = 0; 1577 } 1578 else { 1579 if (SelectedIndex - (rows - 1) < top_index) { 1580 top_index = FocusedItem - (rows - 1); 1581 UpdateTopItem (); 1582 } 1583 1584 selected_index = FocusedItem - (rows - 1); 1585 } 1586 1587 break; 1588 } 1589 default: 1590 break; 1591 } 1592 1593 return selected_index; 1594 } 1595 1596 OnGotFocus(object sender, EventArgs e)1597 private void OnGotFocus (object sender, EventArgs e) 1598 { 1599 if (Items.Count == 0) 1600 return; 1601 1602 if (FocusedItem == -1) 1603 FocusedItem = 0; 1604 1605 InvalidateItem (FocusedItem); 1606 } 1607 OnLostFocus(object sender, EventArgs e)1608 private void OnLostFocus (object sender, EventArgs e) 1609 { 1610 if (FocusedItem != -1) 1611 InvalidateItem (FocusedItem); 1612 } 1613 KeySearch(Keys key)1614 private bool KeySearch (Keys key) 1615 { 1616 char c = (char) key; 1617 if (!Char.IsLetterOrDigit (c)) 1618 return false; 1619 1620 int idx = FindString (c.ToString (), SelectedIndex); 1621 if (idx != ListBox.NoMatches) 1622 SelectedIndex = idx; 1623 1624 return true; 1625 } 1626 HandleKeyDown(Keys key)1627 internal void HandleKeyDown (Keys key) 1628 { 1629 int new_item = -1; 1630 1631 if (Items.Count == 0) 1632 return; 1633 1634 if (KeySearch (key)) 1635 return; 1636 1637 switch (key) { 1638 1639 case Keys.ControlKey: 1640 ctrl_pressed = true; 1641 break; 1642 1643 case Keys.ShiftKey: 1644 shift_pressed = true; 1645 break; 1646 1647 case Keys.Home: 1648 new_item = NavigateItemVisually (ItemNavigation.First); 1649 break; 1650 1651 case Keys.End: 1652 new_item = NavigateItemVisually (ItemNavigation.Last); 1653 break; 1654 1655 case Keys.Up: 1656 new_item = NavigateItemVisually (ItemNavigation.Previous); 1657 break; 1658 1659 case Keys.Down: 1660 new_item = NavigateItemVisually (ItemNavigation.Next); 1661 break; 1662 1663 case Keys.PageUp: 1664 new_item = NavigateItemVisually (ItemNavigation.PreviousPage); 1665 break; 1666 1667 case Keys.PageDown: 1668 new_item = NavigateItemVisually (ItemNavigation.NextPage); 1669 break; 1670 1671 case Keys.Right: 1672 if (multicolumn == true) { 1673 new_item = NavigateItemVisually (ItemNavigation.NextColumn); 1674 } 1675 break; 1676 1677 case Keys.Left: 1678 if (multicolumn == true) { 1679 new_item = NavigateItemVisually (ItemNavigation.PreviousColumn); 1680 } 1681 break; 1682 1683 case Keys.Space: 1684 if (selection_mode == SelectionMode.MultiSimple) { 1685 SelectedItemFromNavigation (FocusedItem); 1686 } 1687 break; 1688 1689 1690 default: 1691 break; 1692 } 1693 1694 if (new_item != -1) { 1695 FocusedItem = new_item; 1696 1697 if (selection_mode != SelectionMode.MultiSimple) 1698 SelectedItemFromNavigation (new_item); 1699 } 1700 } 1701 OnKeyUpLB(object sender, KeyEventArgs e)1702 private void OnKeyUpLB (object sender, KeyEventArgs e) 1703 { 1704 switch (e.KeyCode) { 1705 case Keys.ControlKey: 1706 ctrl_pressed = false; 1707 break; 1708 case Keys.ShiftKey: 1709 shift_pressed = false; 1710 break; 1711 default: 1712 break; 1713 } 1714 } 1715 InvalidateItem(int index)1716 internal void InvalidateItem (int index) 1717 { 1718 if (!IsHandleCreated) 1719 return; 1720 Rectangle bounds = GetItemDisplayRectangle (index, top_index); 1721 if (ClientRectangle.IntersectsWith (bounds)) 1722 Invalidate (bounds); 1723 } 1724 OnItemClick(int index)1725 internal virtual void OnItemClick (int index) 1726 { 1727 OnSelectedIndexChanged (EventArgs.Empty); 1728 OnSelectedValueChanged (EventArgs.Empty); 1729 } 1730 1731 int anchor = -1; 1732 int[] prev_selection; 1733 bool button_pressed = false; 1734 Point button_pressed_loc = new Point (-1, -1); 1735 SelectExtended(int index)1736 private void SelectExtended (int index) 1737 { 1738 SuspendLayout (); 1739 1740 ArrayList new_selection = new ArrayList (); 1741 int start = anchor < index ? anchor : index; 1742 int end = anchor > index ? anchor : index; 1743 for (int i = start; i <= end; i++) 1744 new_selection.Add (i); 1745 1746 if (ctrl_pressed) 1747 foreach (int i in prev_selection) 1748 if (!new_selection.Contains (i)) 1749 new_selection.Add (i); 1750 1751 // Need to make a copy since we can't enumerate and modify the collection 1752 // at the same time 1753 ArrayList sel_indices = (ArrayList) selected_indices.List.Clone (); 1754 foreach (int i in sel_indices) 1755 if (!new_selection.Contains (i)) 1756 selected_indices.Remove (i); 1757 1758 foreach (int i in new_selection) 1759 if (!sel_indices.Contains (i)) 1760 selected_indices.AddCore (i); 1761 ResumeLayout (); 1762 } 1763 OnMouseDownLB(object sender, MouseEventArgs e)1764 private void OnMouseDownLB (object sender, MouseEventArgs e) 1765 { 1766 // Only do stuff with the left mouse button 1767 if ((e.Button & MouseButtons.Left) == 0) 1768 return; 1769 1770 int index = IndexAtClientPoint (e.X, e.Y); 1771 if (index == -1) 1772 return; 1773 1774 switch (SelectionMode) { 1775 case SelectionMode.One: 1776 SelectedIndices.AddCore (index); // Unselects previous one 1777 break; 1778 1779 case SelectionMode.MultiSimple: 1780 if (SelectedIndices.Contains (index)) 1781 SelectedIndices.RemoveCore (index); 1782 else 1783 SelectedIndices.AddCore (index); 1784 break; 1785 1786 case SelectionMode.MultiExtended: 1787 shift_pressed = (XplatUI.State.ModifierKeys & Keys.Shift) != 0; 1788 ctrl_pressed = (XplatUI.State.ModifierKeys & Keys.Control) != 0; 1789 1790 if (shift_pressed) { 1791 SelectedIndices.ClearCore (); 1792 SelectExtended (index); 1793 break; 1794 } 1795 1796 anchor = index; 1797 1798 if (ctrl_pressed) { 1799 prev_selection = new int [SelectedIndices.Count]; 1800 SelectedIndices.CopyTo (prev_selection, 0); 1801 1802 if (SelectedIndices.Contains (index)) 1803 SelectedIndices.RemoveCore (index); 1804 else 1805 SelectedIndices.AddCore (index); 1806 1807 break; 1808 } 1809 1810 SelectedIndices.ClearCore (); 1811 SelectedIndices.AddCore (index); 1812 break; 1813 1814 case SelectionMode.None: 1815 break; 1816 default: 1817 return; 1818 } 1819 1820 button_pressed = true; 1821 button_pressed_loc = new Point (e.X, e.Y); 1822 FocusedItem = index; 1823 } 1824 OnMouseMoveLB(object sender, MouseEventArgs e)1825 private void OnMouseMoveLB (object sender, MouseEventArgs e) 1826 { 1827 // Don't take into account MouseMove events generated with MouseDown 1828 if (!button_pressed || button_pressed_loc == new Point (e.X, e.Y)) 1829 return; 1830 1831 int index = IndexAtClientPoint (e.X, e.Y); 1832 if (index == -1) 1833 return; 1834 1835 switch (SelectionMode) { 1836 case SelectionMode.One: 1837 SelectedIndices.AddCore (index); // Unselects previous one 1838 break; 1839 1840 case SelectionMode.MultiSimple: 1841 break; 1842 1843 case SelectionMode.MultiExtended: 1844 SelectExtended (index); 1845 break; 1846 1847 case SelectionMode.None: 1848 break; 1849 default: 1850 return; 1851 } 1852 1853 FocusedItem = index; 1854 } 1855 OnDragDropEnd(DragDropEffects effects)1856 internal override void OnDragDropEnd (DragDropEffects effects) 1857 { 1858 // In the case of a DnD operation (started on MouseDown) 1859 // there will be no MouseUp event, so we need to reset 1860 // the state here 1861 button_pressed = false; 1862 } 1863 OnMouseUpLB(object sender, MouseEventArgs e)1864 private void OnMouseUpLB (object sender, MouseEventArgs e) 1865 { 1866 // Only do stuff with the left mouse button 1867 if ((e.Button & MouseButtons.Left) == 0) 1868 return; 1869 1870 if (e.Clicks > 1) { 1871 OnDoubleClick (EventArgs.Empty); 1872 OnMouseDoubleClick (e); 1873 } 1874 else if (e.Clicks == 1) { 1875 OnClick (EventArgs.Empty); 1876 OnMouseClick (e); 1877 } 1878 1879 if (!button_pressed) 1880 return; 1881 1882 int index = IndexAtClientPoint (e.X, e.Y); 1883 OnItemClick (index); 1884 button_pressed = ctrl_pressed = shift_pressed = false; 1885 } 1886 Scroll(ScrollBar scrollbar, int delta)1887 private void Scroll (ScrollBar scrollbar, int delta) 1888 { 1889 if (delta == 0 || !scrollbar.Visible || !scrollbar.Enabled) 1890 return; 1891 1892 int max; 1893 if (scrollbar == hscrollbar) 1894 max = hscrollbar.Maximum - (items_area.Width / ColumnWidthInternal) + 1; 1895 else 1896 max = vscrollbar.Maximum - (items_area.Height / ItemHeight) + 1; 1897 1898 int val = scrollbar.Value + delta; 1899 if (val > max) 1900 val = max; 1901 else if (val < scrollbar.Minimum) 1902 val = scrollbar.Minimum; 1903 scrollbar.Value = val; 1904 } 1905 OnMouseWheelLB(object sender, MouseEventArgs me)1906 private void OnMouseWheelLB (object sender, MouseEventArgs me) 1907 { 1908 if (Items.Count == 0) 1909 return; 1910 1911 int lines = me.Delta / 120; 1912 1913 if (MultiColumn) 1914 Scroll (hscrollbar, -SystemInformation.MouseWheelScrollLines * lines); 1915 else 1916 Scroll (vscrollbar, -lines); 1917 } 1918 OnPaintInternal(PaintEventArgs pevent)1919 internal override void OnPaintInternal (PaintEventArgs pevent) 1920 { 1921 if (suspend_layout) 1922 return; 1923 1924 Draw (pevent.ClipRectangle, pevent.Graphics); 1925 } 1926 RepositionScrollBars()1927 internal void RepositionScrollBars () 1928 { 1929 if (vscrollbar.is_visible) { 1930 vscrollbar.Size = new Size (vscrollbar.Width, items_area.Height); 1931 vscrollbar.Location = new Point (items_area.Width, 0); 1932 } 1933 1934 if (hscrollbar.is_visible) { 1935 hscrollbar.Size = new Size (items_area.Width, hscrollbar.Height); 1936 hscrollbar.Location = new Point (0, items_area.Height); 1937 } 1938 } 1939 1940 // An item navigation operation (mouse or keyboard) has caused to select a new item SelectedItemFromNavigation(int index)1941 internal void SelectedItemFromNavigation (int index) 1942 { 1943 switch (SelectionMode) { 1944 case SelectionMode.None: 1945 // .Net doesn't select the item, only ensures that it's visible 1946 // and fires the selection related events 1947 EnsureVisible (index); 1948 OnSelectedIndexChanged (EventArgs.Empty); 1949 OnSelectedValueChanged (EventArgs.Empty); 1950 break; 1951 case SelectionMode.One: { 1952 SelectedIndex = index; 1953 break; 1954 } 1955 case SelectionMode.MultiSimple: { 1956 if (SelectedIndex == -1) { 1957 SelectedIndex = index; 1958 } else { 1959 1960 if (SelectedIndices.Contains (index)) 1961 SelectedIndices.Remove (index); 1962 else { 1963 SelectedIndices.AddCore (index); 1964 1965 OnSelectedIndexChanged (EventArgs.Empty); 1966 OnSelectedValueChanged (EventArgs.Empty); 1967 } 1968 } 1969 break; 1970 } 1971 1972 case SelectionMode.MultiExtended: { 1973 if (SelectedIndex == -1) { 1974 SelectedIndex = index; 1975 } else { 1976 1977 if (ctrl_pressed == false && shift_pressed == false) { 1978 SelectedIndices.Clear (); 1979 } 1980 1981 if (shift_pressed == true) { 1982 ShiftSelection (index); 1983 } else { // ctrl_pressed or single item 1984 SelectedIndices.AddCore (index); 1985 1986 } 1987 1988 OnSelectedIndexChanged (EventArgs.Empty); 1989 OnSelectedValueChanged (EventArgs.Empty); 1990 } 1991 break; 1992 } 1993 1994 default: 1995 break; 1996 } 1997 } 1998 ShiftSelection(int index)1999 private void ShiftSelection (int index) 2000 { 2001 int shorter_item = -1, dist = Items.Count + 1, cur_dist; 2002 2003 foreach (int idx in selected_indices) { 2004 if (idx > index) { 2005 cur_dist = idx - index; 2006 } else { 2007 cur_dist = index - idx; 2008 } 2009 2010 if (cur_dist < dist) { 2011 dist = cur_dist; 2012 shorter_item = idx; 2013 } 2014 } 2015 2016 if (shorter_item != -1) { 2017 int start, end; 2018 2019 if (shorter_item > index) { 2020 start = index; 2021 end = shorter_item; 2022 } else { 2023 start = shorter_item; 2024 end = index; 2025 } 2026 2027 selected_indices.Clear (); 2028 for (int idx = start; idx <= end; idx++) { 2029 selected_indices.AddCore (idx); 2030 } 2031 } 2032 } 2033 2034 internal int FocusedItem { 2035 get { return focused_item; } 2036 set { 2037 if (focused_item == value) 2038 return; 2039 2040 int prev = focused_item; 2041 2042 focused_item = value; 2043 2044 if (has_focus == false) 2045 return; 2046 2047 if (prev != -1) 2048 InvalidateItem (prev); 2049 2050 if (value != -1) 2051 InvalidateItem (value); 2052 2053 // UIA Framework: Generates FocusedItemChanged event. 2054 OnUIAFocusedItemChangedEvent (); 2055 } 2056 } 2057 2058 StringFormat string_format; 2059 internal StringFormat StringFormat { 2060 get { 2061 if (string_format == null) { 2062 string_format = new StringFormat (); 2063 string_format.FormatFlags = StringFormatFlags.NoWrap; 2064 2065 if (RightToLeft == RightToLeft.Yes) 2066 string_format.Alignment = StringAlignment.Far; 2067 else 2068 string_format.Alignment = StringAlignment.Near; 2069 CalculateTabStops (); 2070 } 2071 return string_format; 2072 } 2073 } 2074 CollectionChanged()2075 internal virtual void CollectionChanged () 2076 { 2077 if (sorted) 2078 Sort (false); 2079 2080 if (Items.Count == 0) { 2081 selected_indices.List.Clear (); 2082 focused_item = -1; 2083 top_index = 0; 2084 } 2085 if (Items.Count <= focused_item) 2086 focused_item = Items.Count - 1; 2087 2088 if (!IsHandleCreated || suspend_layout) 2089 return; 2090 2091 LayoutListBox (); 2092 2093 base.Refresh (); 2094 } 2095 EnsureVisible(int index)2096 void EnsureVisible (int index) 2097 { 2098 if (!IsHandleCreated || index == -1) 2099 return; 2100 2101 if (index < top_index) { 2102 top_index = index; 2103 UpdateTopItem (); 2104 Invalidate (); 2105 } else if (!multicolumn) { 2106 int rows = items_area.Height / ItemHeight; 2107 if (index >= (top_index + rows)) 2108 top_index = index - rows + 1; 2109 2110 UpdateTopItem (); 2111 } else { 2112 int rows = Math.Max (1, items_area.Height / ItemHeight); 2113 int cols = Math.Max (1, items_area.Width / ColumnWidthInternal); 2114 2115 if (index >= (top_index + (rows * cols))) { 2116 int incolumn = index / rows; 2117 top_index = (incolumn - (cols - 1)) * rows; 2118 2119 UpdateTopItem (); 2120 Invalidate (); 2121 } 2122 } 2123 } 2124 UpdateListBoxBounds()2125 private void UpdateListBoxBounds () 2126 { 2127 if (IsHandleCreated) 2128 SetBoundsInternal (bounds.X, bounds.Y, bounds.Width, IntegralHeight ? SnapHeightToIntegral (requested_height) : requested_height, BoundsSpecified.None); 2129 } 2130 UpdateScrollBars()2131 private void UpdateScrollBars () 2132 { 2133 items_area = ClientRectangle; 2134 if (UpdateHorizontalScrollBar ()) { 2135 items_area.Height -= hscrollbar.Height; 2136 if (UpdateVerticalScrollBar ()) { 2137 items_area.Width -= vscrollbar.Width; 2138 UpdateHorizontalScrollBar (); 2139 } 2140 } else if (UpdateVerticalScrollBar ()) { 2141 items_area.Width -= vscrollbar.Width; 2142 if (UpdateHorizontalScrollBar ()) { 2143 items_area.Height -= hscrollbar.Height; 2144 UpdateVerticalScrollBar (); 2145 } 2146 } 2147 2148 RepositionScrollBars (); 2149 } 2150 2151 /* Determines if the horizontal scrollbar has to be displyed */ UpdateHorizontalScrollBar()2152 private bool UpdateHorizontalScrollBar () 2153 { 2154 bool show = false; 2155 bool enabled = true; 2156 2157 if (MultiColumn) { 2158 if (canvas_size.Width > items_area.Width) { 2159 show = true; 2160 hscrollbar.Maximum = canvas_size.Width / ColumnWidthInternal - 1; 2161 } else if (ScrollAlwaysVisible == true) { 2162 enabled = false; 2163 show = true; 2164 hscrollbar.Maximum = 0; 2165 } 2166 } else if (canvas_size.Width > ClientRectangle.Width && HorizontalScrollbar) { 2167 show = true; 2168 hscrollbar.Maximum = canvas_size.Width; 2169 hscrollbar.LargeChange = Math.Max (0, items_area.Width); 2170 } else if (scroll_always_visible && horizontal_scrollbar) { 2171 show = true; 2172 enabled = false; 2173 hscrollbar.Maximum = 0; 2174 } 2175 2176 hbar_offset = hscrollbar.Value; 2177 hscrollbar.Enabled = enabled; 2178 hscrollbar.Visible = show; 2179 2180 return show; 2181 } 2182 2183 /* Determines if the vertical scrollbar has to be displyed */ UpdateVerticalScrollBar()2184 private bool UpdateVerticalScrollBar () 2185 { 2186 if (MultiColumn || (Items.Count == 0 && !scroll_always_visible)) { 2187 vscrollbar.Visible = false; 2188 return false; 2189 } else if (Items.Count == 0) { 2190 vscrollbar.Visible = true; 2191 vscrollbar.Enabled = false; 2192 vscrollbar.Maximum = 0; 2193 return true; 2194 } 2195 2196 bool show = false; 2197 bool enabled = true; 2198 if (canvas_size.Height > items_area.Height) { 2199 show = true; 2200 vscrollbar.Maximum = Items.Count - 1; 2201 vscrollbar.LargeChange = Math.Max (items_area.Height / ItemHeight, 0); 2202 } else if (ScrollAlwaysVisible) { 2203 show = true; 2204 enabled = false; 2205 vscrollbar.Maximum = 0; 2206 } 2207 2208 vscrollbar.Enabled = enabled; 2209 vscrollbar.Visible = show; 2210 2211 return show; 2212 } 2213 2214 // Value Changed VerticalScrollEvent(object sender, EventArgs e)2215 private void VerticalScrollEvent (object sender, EventArgs e) 2216 { 2217 int top_item = top_index; 2218 2219 top_index = /*row_count + */ vscrollbar.Value; 2220 last_visible_index = LastVisibleItem (); 2221 2222 int delta = (top_item - top_index) * ItemHeight; 2223 if (DrawMode == DrawMode.OwnerDrawVariable) { 2224 delta = 0; 2225 2226 if (top_index < top_item) 2227 for (int i = top_index; i < top_item; i++) 2228 delta += GetItemHeight (i); 2229 else 2230 for (int i = top_item; i < top_index; i++) 2231 delta -= GetItemHeight (i); 2232 } 2233 2234 if (IsHandleCreated) 2235 XplatUI.ScrollWindow (Handle, items_area, 0, delta, false); 2236 } 2237 2238 #endregion Private Methods 2239 2240 public class IntegerCollection : IList, ICollection, IEnumerable 2241 { 2242 private ListBox owner; 2243 private List<int> list; 2244 2245 #region Public Constructor IntegerCollection(ListBox owner)2246 public IntegerCollection (ListBox owner) 2247 { 2248 this.owner = owner; 2249 list = new List<int> (); 2250 } 2251 #endregion 2252 2253 #region Public Properties 2254 [Browsable (false)] 2255 public int Count { 2256 get { return list.Count; } 2257 } 2258 2259 public int this [int index] { 2260 get { return list[index]; } 2261 set { list[index] = value; owner.CalculateTabStops (); } 2262 } 2263 #endregion 2264 2265 #region Public Methods Add(int item)2266 public int Add (int item) 2267 { 2268 // This collection does not allow duplicates 2269 if (!list.Contains (item)) { 2270 list.Add (item); 2271 list.Sort (); 2272 owner.CalculateTabStops (); 2273 } 2274 2275 return list.IndexOf (item); 2276 } 2277 AddRange(int[] items)2278 public void AddRange (int[] items) 2279 { 2280 AddItems (items); 2281 } 2282 AddRange(IntegerCollection value)2283 public void AddRange (IntegerCollection value) 2284 { 2285 AddItems (value); 2286 } 2287 AddItems(IList items)2288 void AddItems (IList items) 2289 { 2290 if (items == null) 2291 throw new ArgumentNullException ("items"); 2292 2293 foreach (int i in items) 2294 if (!list.Contains (i)) 2295 list.Add (i); 2296 2297 list.Sort (); 2298 } 2299 Clear()2300 public void Clear () 2301 { 2302 list.Clear (); 2303 owner.CalculateTabStops (); 2304 } 2305 Contains(int item)2306 public bool Contains (int item) 2307 { 2308 return list.Contains (item); 2309 } 2310 CopyTo(Array destination, int index)2311 public void CopyTo (Array destination, int index) 2312 { 2313 for (int i = 0; i < list.Count; i++) 2314 destination.SetValue (list[i], index++); 2315 } 2316 IndexOf(int item)2317 public int IndexOf (int item) 2318 { 2319 return list.IndexOf (item); 2320 } 2321 Remove(int item)2322 public void Remove (int item) 2323 { 2324 list.Remove (item); 2325 list.Sort (); 2326 owner.CalculateTabStops (); 2327 } 2328 RemoveAt(int index)2329 public void RemoveAt (int index) 2330 { 2331 if (index < 0) 2332 throw new IndexOutOfRangeException (); 2333 2334 list.RemoveAt (index); 2335 list.Sort (); 2336 owner.CalculateTabStops (); 2337 } 2338 #endregion 2339 2340 #region IEnumerable Members IEnumerable.GetEnumerator()2341 IEnumerator IEnumerable.GetEnumerator () 2342 { 2343 return list.GetEnumerator (); 2344 } 2345 #endregion 2346 2347 #region IList Members IList.Add(object item)2348 int IList.Add (object item) 2349 { 2350 int? intValue = item as int?; 2351 if (!intValue.HasValue) 2352 throw new ArgumentException ("item"); 2353 return Add (intValue.Value); 2354 } 2355 IList.Clear()2356 void IList.Clear () 2357 { 2358 Clear (); 2359 } 2360 IList.Contains(object item)2361 bool IList.Contains (object item) 2362 { 2363 int? intValue = item as int?; 2364 if (!intValue.HasValue) 2365 return false; 2366 return Contains (intValue.Value); 2367 } 2368 IList.IndexOf(object item)2369 int IList.IndexOf (object item) 2370 { 2371 int? intValue = item as int?; 2372 if (!intValue.HasValue) 2373 return -1; 2374 return IndexOf (intValue.Value); 2375 } 2376 IList.Insert(int index, object value)2377 void IList.Insert (int index, object value) 2378 { 2379 throw new NotSupportedException (string.Format ( 2380 CultureInfo.InvariantCulture, "No items " 2381 + "can be inserted into {0}, since it is" 2382 + " a sorted collection.", this.GetType ())); 2383 } 2384 2385 bool IList.IsFixedSize 2386 { 2387 get { return false; } 2388 } 2389 2390 bool IList.IsReadOnly 2391 { 2392 get { return false; } 2393 } 2394 IList.Remove(object value)2395 void IList.Remove (object value) 2396 { 2397 int? intValue = value as int?; 2398 if (!intValue.HasValue) 2399 throw new ArgumentException ("value"); 2400 2401 Remove (intValue.Value); 2402 } 2403 IList.RemoveAt(int index)2404 void IList.RemoveAt (int index) 2405 { 2406 RemoveAt (index); 2407 } 2408 2409 object IList.this[int index] { 2410 get { return this[index]; } 2411 set { this[index] = (int)value; } 2412 } 2413 #endregion 2414 2415 #region ICollection Members 2416 bool ICollection.IsSynchronized { 2417 get { return true; } 2418 } 2419 2420 object ICollection.SyncRoot { 2421 get { return this; } 2422 } 2423 #endregion 2424 } 2425 2426 [ListBindable (false)] 2427 public class ObjectCollection : IList, ICollection, IEnumerable 2428 { 2429 internal class ListObjectComparer : IComparer 2430 { Compare(object a, object b)2431 public int Compare (object a, object b) 2432 { 2433 string str1 = a.ToString (); 2434 string str2 = b.ToString (); 2435 return str1.CompareTo (str2); 2436 } 2437 } 2438 2439 private ListBox owner; 2440 internal ArrayList object_items = new ArrayList (); 2441 2442 #region UIA Framework Events 2443 //NOTE: 2444 // We are using Reflection to add/remove internal events. 2445 // Class ListProvider uses the events. 2446 // 2447 //Event used to generate UIA StructureChangedEvent 2448 static object UIACollectionChangedEvent = new object (); 2449 2450 internal event CollectionChangeEventHandler UIACollectionChanged { 2451 add { owner.Events.AddHandler (UIACollectionChangedEvent, value); } 2452 remove { owner.Events.RemoveHandler (UIACollectionChangedEvent, value); } 2453 } 2454 OnUIACollectionChangedEvent(CollectionChangeEventArgs args)2455 internal void OnUIACollectionChangedEvent (CollectionChangeEventArgs args) 2456 { 2457 CollectionChangeEventHandler eh 2458 = (CollectionChangeEventHandler) owner.Events [UIACollectionChangedEvent]; 2459 if (eh != null) 2460 eh (owner, args); 2461 } 2462 #endregion UIA Framework Events 2463 ObjectCollection(ListBox owner)2464 public ObjectCollection (ListBox owner) 2465 { 2466 this.owner = owner; 2467 } 2468 ObjectCollection(ListBox owner, object[] value)2469 public ObjectCollection (ListBox owner, object[] value) 2470 { 2471 this.owner = owner; 2472 AddRange (value); 2473 } 2474 ObjectCollection(ListBox owner, ObjectCollection value)2475 public ObjectCollection (ListBox owner, ObjectCollection value) 2476 { 2477 this.owner = owner; 2478 AddRange (value); 2479 } 2480 2481 #region Public Properties 2482 public int Count { 2483 get { return object_items.Count; } 2484 } 2485 2486 public bool IsReadOnly { 2487 get { return false; } 2488 } 2489 2490 [Browsable(false)] 2491 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 2492 public virtual object this [int index] { 2493 get { 2494 if (index < 0 || index >= Count) 2495 throw new ArgumentOutOfRangeException ("Index of out range"); 2496 2497 return object_items[index]; 2498 } 2499 set { 2500 if (index < 0 || index >= Count) 2501 throw new ArgumentOutOfRangeException ("Index of out range"); 2502 if (value == null) 2503 throw new ArgumentNullException ("value"); 2504 2505 //UIA Framework event: Item Removed 2506 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, object_items [index])); 2507 2508 object_items[index] = value; 2509 2510 //UIA Framework event: Item Added 2511 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, value)); 2512 2513 owner.CollectionChanged (); 2514 } 2515 } 2516 2517 bool ICollection.IsSynchronized { 2518 get { return false; } 2519 } 2520 2521 object ICollection.SyncRoot { 2522 get { return this; } 2523 } 2524 2525 bool IList.IsFixedSize { 2526 get { return false; } 2527 } 2528 2529 #endregion Public Properties 2530 2531 #region Public Methods Add(object item)2532 public int Add (object item) 2533 { 2534 int idx; 2535 object[] selectedItems = null; 2536 2537 // we need to remember the original selected items so that we can update the indices 2538 if (owner.sorted) { 2539 selectedItems = new object[owner.SelectedItems.Count]; 2540 owner.SelectedItems.CopyTo (selectedItems, 0); 2541 } 2542 2543 idx = AddItem (item); 2544 owner.CollectionChanged (); 2545 2546 // If we are sorted, the item probably moved indexes, get the real one 2547 if (owner.sorted) { 2548 // update indices of selected items 2549 owner.SelectedIndices.Clear (); 2550 for (int i = 0; i < selectedItems.Length; i++) { 2551 owner.SelectedIndex = this.IndexOf (selectedItems [i]); 2552 } 2553 return this.IndexOf (item); 2554 } 2555 2556 return idx; 2557 } 2558 AddRange(object[] items)2559 public void AddRange (object[] items) 2560 { 2561 AddItems (items); 2562 } 2563 AddRange(ObjectCollection value)2564 public void AddRange (ObjectCollection value) 2565 { 2566 AddItems (value); 2567 } 2568 AddItems(IList items)2569 internal void AddItems (IList items) 2570 { 2571 if (items == null) 2572 throw new ArgumentNullException ("items"); 2573 2574 foreach (object mi in items) 2575 AddItem (mi); 2576 2577 owner.CollectionChanged (); 2578 } 2579 Clear()2580 public virtual void Clear () 2581 { 2582 owner.selected_indices.ClearCore (); 2583 object_items.Clear (); 2584 owner.CollectionChanged (); 2585 2586 //UIA Framework event: Items list cleared 2587 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, null)); 2588 } 2589 Contains(object value)2590 public bool Contains (object value) 2591 { 2592 if (value == null) 2593 throw new ArgumentNullException ("value"); 2594 2595 return object_items.Contains (value); 2596 } 2597 CopyTo(object[] destination, int arrayIndex)2598 public void CopyTo (object[] destination, int arrayIndex) 2599 { 2600 object_items.CopyTo (destination, arrayIndex); 2601 } 2602 ICollection.CopyTo(Array destination, int index)2603 void ICollection.CopyTo (Array destination, int index) 2604 { 2605 object_items.CopyTo (destination, index); 2606 } 2607 GetEnumerator()2608 public IEnumerator GetEnumerator () 2609 { 2610 return object_items.GetEnumerator (); 2611 } 2612 IList.Add(object item)2613 int IList.Add (object item) 2614 { 2615 return Add (item); 2616 } 2617 IndexOf(object value)2618 public int IndexOf (object value) 2619 { 2620 if (value == null) 2621 throw new ArgumentNullException ("value"); 2622 2623 return object_items.IndexOf (value); 2624 } 2625 Insert(int index, object item)2626 public void Insert (int index, object item) 2627 { 2628 if (index < 0 || index > Count) 2629 throw new ArgumentOutOfRangeException ("Index of out range"); 2630 if (item == null) 2631 throw new ArgumentNullException ("item"); 2632 2633 owner.BeginUpdate (); 2634 object_items.Insert (index, item); 2635 owner.CollectionChanged (); 2636 owner.EndUpdate (); 2637 2638 //UIA Framework event: Item Added 2639 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, item)); 2640 } 2641 Remove(object value)2642 public void Remove (object value) 2643 { 2644 if (value == null) 2645 return; 2646 2647 int index = IndexOf (value); 2648 if (index != -1) 2649 RemoveAt (index); 2650 } 2651 RemoveAt(int index)2652 public void RemoveAt (int index) 2653 { 2654 if (index < 0 || index >= Count) 2655 throw new ArgumentOutOfRangeException ("Index of out range"); 2656 2657 //UIA Framework element removed 2658 object removed = object_items [index]; 2659 UpdateSelection (index); 2660 object_items.RemoveAt (index); 2661 owner.CollectionChanged (); 2662 2663 //UIA Framework event: Item Removed 2664 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, removed)); 2665 } 2666 #endregion Public Methods 2667 2668 #region Private Methods AddItem(object item)2669 internal int AddItem (object item) 2670 { 2671 if (item == null) 2672 throw new ArgumentNullException ("item"); 2673 2674 int cnt = object_items.Count; 2675 object_items.Add (item); 2676 2677 //UIA Framework event: Item Added 2678 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, item)); 2679 2680 return cnt; 2681 } 2682 2683 // we receive the index to be removed UpdateSelection(int removed_index)2684 void UpdateSelection (int removed_index) 2685 { 2686 owner.selected_indices.Remove (removed_index); 2687 2688 if (owner.selection_mode != SelectionMode.None) { 2689 int last_idx = object_items.Count - 1; 2690 2691 // if the last item was selected, remove it from selection, 2692 // since it will become invalid after the removal 2693 if (owner.selected_indices.Contains (last_idx)) { 2694 owner.selected_indices.Remove (last_idx); 2695 2696 // in SelectionMode.One try to put the selection on the new last item 2697 int new_idx = last_idx - 1; 2698 if (owner.selection_mode == SelectionMode.One && new_idx > -1) 2699 owner.selected_indices.Add (new_idx); 2700 } 2701 } 2702 2703 } 2704 Sort()2705 internal void Sort () 2706 { 2707 object_items.Sort (new ListObjectComparer ()); 2708 } 2709 2710 #endregion Private Methods 2711 } 2712 2713 public class SelectedIndexCollection : IList, ICollection, IEnumerable 2714 { 2715 private ListBox owner; 2716 ArrayList selection; 2717 bool sorting_needed; // Selection state retrieval is done sorted - we do it lazyly 2718 2719 #region UIA Framework Events 2720 2721 //NOTE: 2722 // We are using Reflection to add/remove internal events. 2723 // Class ListProvider uses the events. 2724 // 2725 //Event used to generate UIA StructureChangedEvent 2726 static object UIACollectionChangedEvent = new object (); 2727 2728 internal event CollectionChangeEventHandler UIACollectionChanged { 2729 add { owner.Events.AddHandler (UIACollectionChangedEvent, value); } 2730 remove { owner.Events.RemoveHandler (UIACollectionChangedEvent, value); } 2731 } 2732 OnUIACollectionChangedEvent(CollectionChangeEventArgs args)2733 internal void OnUIACollectionChangedEvent (CollectionChangeEventArgs args) 2734 { 2735 CollectionChangeEventHandler eh 2736 = (CollectionChangeEventHandler) owner.Events [UIACollectionChangedEvent]; 2737 if (eh != null) 2738 eh (owner, args); 2739 } 2740 2741 #endregion UIA Framework Events 2742 2743 SelectedIndexCollection(ListBox owner)2744 public SelectedIndexCollection (ListBox owner) 2745 { 2746 this.owner = owner; 2747 selection = new ArrayList (); 2748 } 2749 2750 #region Public Properties 2751 [Browsable (false)] 2752 public int Count { 2753 get { return selection.Count; } 2754 } 2755 2756 public bool IsReadOnly { 2757 get { return true; } 2758 } 2759 2760 public int this [int index] { 2761 get { 2762 if (index < 0 || index >= Count) 2763 throw new ArgumentOutOfRangeException ("Index of out range"); 2764 2765 CheckSorted (); 2766 return (int)selection [index]; 2767 } 2768 } 2769 2770 bool ICollection.IsSynchronized { 2771 get { return true; } 2772 } 2773 2774 bool IList.IsFixedSize{ 2775 get { return true; } 2776 } 2777 2778 object ICollection.SyncRoot { 2779 get { return selection; } 2780 } 2781 2782 #endregion Public Properties 2783 2784 #region Public Methods Add(int index)2785 public void Add (int index) 2786 { 2787 if (AddCore (index)) { 2788 owner.OnSelectedIndexChanged (EventArgs.Empty); 2789 owner.OnSelectedValueChanged (EventArgs.Empty); 2790 } 2791 } 2792 2793 // Need to separate selection logic from events, 2794 // since selection changes using keys/mouse handle them their own way AddCore(int index)2795 internal bool AddCore (int index) 2796 { 2797 if (selection.Contains (index)) 2798 return false; 2799 2800 if (index == -1) // Weird MS behaviour 2801 return false; 2802 if (index < -1 || index >= owner.Items.Count) 2803 throw new ArgumentOutOfRangeException ("index"); 2804 if (owner.selection_mode == SelectionMode.None) 2805 throw new InvalidOperationException ("Cannot call this method when selection mode is SelectionMode.None"); 2806 2807 if (owner.selection_mode == SelectionMode.One && Count > 0) // Unselect previously selected item 2808 RemoveCore ((int)selection [0]); 2809 2810 selection.Add (index); 2811 sorting_needed = true; 2812 owner.EnsureVisible (index); 2813 owner.FocusedItem = index; 2814 owner.InvalidateItem (index); 2815 2816 // UIA Framework event: Selected item added 2817 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Add, index)); 2818 2819 return true; 2820 } 2821 Clear()2822 public void Clear () 2823 { 2824 if (ClearCore ()) { 2825 owner.OnSelectedIndexChanged (EventArgs.Empty); 2826 owner.OnSelectedValueChanged (EventArgs.Empty); 2827 } 2828 } 2829 ClearCore()2830 internal bool ClearCore () 2831 { 2832 if (selection.Count == 0) 2833 return false; 2834 2835 foreach (int index in selection) 2836 owner.InvalidateItem (index); 2837 2838 selection.Clear (); 2839 2840 // UIA Framework event: Selected items list updated 2841 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Refresh, -1)); 2842 2843 return true; 2844 } 2845 Contains(int selectedIndex)2846 public bool Contains (int selectedIndex) 2847 { 2848 foreach (int index in selection) 2849 if (index == selectedIndex) 2850 return true; 2851 return false; 2852 } 2853 CopyTo(Array destination, int index)2854 public void CopyTo (Array destination, int index) 2855 { 2856 CheckSorted (); 2857 selection.CopyTo (destination, index); 2858 } 2859 GetEnumerator()2860 public IEnumerator GetEnumerator () 2861 { 2862 CheckSorted (); 2863 return selection.GetEnumerator (); 2864 } 2865 2866 // FIXME: Probably we can avoid sorting when calling 2867 // IndexOf (imagine a scenario where multiple removal of items 2868 // happens) Remove(int index)2869 public void Remove (int index) 2870 { 2871 // Separate logic from events here too 2872 if (RemoveCore (index)) { 2873 owner.OnSelectedIndexChanged (EventArgs.Empty); 2874 owner.OnSelectedValueChanged (EventArgs.Empty); 2875 } 2876 } 2877 RemoveCore(int index)2878 internal bool RemoveCore (int index) 2879 { 2880 int idx = IndexOf (index); 2881 if (idx == -1) 2882 return false; 2883 2884 selection.RemoveAt (idx); 2885 owner.InvalidateItem (index); 2886 2887 // UIA Framework event: Selected item removed from selection 2888 OnUIACollectionChangedEvent (new CollectionChangeEventArgs (CollectionChangeAction.Remove, index)); 2889 2890 return true; 2891 } 2892 IList.Add(object value)2893 int IList.Add (object value) 2894 { 2895 throw new NotSupportedException (); 2896 } 2897 IList.Clear()2898 void IList.Clear () 2899 { 2900 throw new NotSupportedException (); 2901 } 2902 IList.Contains(object selectedIndex)2903 bool IList.Contains (object selectedIndex) 2904 { 2905 return Contains ((int)selectedIndex); 2906 } 2907 IList.IndexOf(object selectedIndex)2908 int IList.IndexOf (object selectedIndex) 2909 { 2910 return IndexOf ((int) selectedIndex); 2911 } 2912 IList.Insert(int index, object value)2913 void IList.Insert (int index, object value) 2914 { 2915 throw new NotSupportedException (); 2916 } 2917 IList.Remove(object value)2918 void IList.Remove (object value) 2919 { 2920 throw new NotSupportedException (); 2921 } 2922 IList.RemoveAt(int index)2923 void IList.RemoveAt (int index) 2924 { 2925 throw new NotSupportedException (); 2926 } 2927 2928 object IList.this[int index]{ 2929 get { return this [index]; } 2930 set {throw new NotImplementedException (); } 2931 } 2932 IndexOf(int selectedIndex)2933 public int IndexOf (int selectedIndex) 2934 { 2935 CheckSorted (); 2936 2937 for (int i = 0; i < selection.Count; i++) 2938 if ((int)selection [i] == selectedIndex) 2939 return i; 2940 2941 return -1; 2942 } 2943 #endregion Public Methods 2944 internal ArrayList List { 2945 get { 2946 CheckSorted (); 2947 return selection; 2948 } 2949 } 2950 CheckSorted()2951 void CheckSorted () 2952 { 2953 if (sorting_needed) { 2954 sorting_needed = false; 2955 selection.Sort (); 2956 } 2957 } 2958 } 2959 2960 public class SelectedObjectCollection : IList, ICollection, IEnumerable 2961 { 2962 private ListBox owner; 2963 SelectedObjectCollection(ListBox owner)2964 public SelectedObjectCollection (ListBox owner) 2965 { 2966 this.owner = owner; 2967 } 2968 2969 #region Public Properties 2970 public int Count { 2971 get { return owner.selected_indices.Count; } 2972 } 2973 2974 public bool IsReadOnly { 2975 get { return true; } 2976 } 2977 2978 [Browsable(false)] 2979 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] 2980 public object this [int index] { 2981 get { 2982 if (index < 0 || index >= Count) 2983 throw new ArgumentOutOfRangeException ("Index of out range"); 2984 2985 return owner.items [owner.selected_indices [index]]; 2986 } 2987 set {throw new NotSupportedException ();} 2988 } 2989 2990 bool ICollection.IsSynchronized { 2991 get { return true; } 2992 } 2993 2994 object ICollection.SyncRoot { 2995 get { return this; } 2996 } 2997 2998 bool IList.IsFixedSize { 2999 get { return true; } 3000 } 3001 3002 #endregion Public Properties 3003 3004 #region Public Methods Add(object value)3005 public void Add (object value) 3006 { 3007 if (owner.selection_mode == SelectionMode.None) 3008 throw new ArgumentException ("Cannot call this method if SelectionMode is SelectionMode.None"); 3009 3010 int idx = owner.items.IndexOf (value); 3011 if (idx == -1) 3012 return; 3013 3014 owner.selected_indices.Add (idx); 3015 } 3016 Clear()3017 public void Clear () 3018 { 3019 owner.selected_indices.Clear (); 3020 } 3021 Contains(object selectedObject)3022 public bool Contains (object selectedObject) 3023 { 3024 int idx = owner.items.IndexOf (selectedObject); 3025 return idx == -1 ? false : owner.selected_indices.Contains (idx); 3026 } 3027 CopyTo(Array destination, int index)3028 public void CopyTo (Array destination, int index) 3029 { 3030 for (int i = 0; i < Count; i++) 3031 destination.SetValue (this [i], index++); 3032 } 3033 Remove(object value)3034 public void Remove (object value) 3035 { 3036 if (value == null) 3037 return; 3038 3039 int idx = owner.items.IndexOf (value); 3040 if (idx == -1) 3041 return; 3042 3043 owner.selected_indices.Remove (idx); 3044 } 3045 IList.Add(object value)3046 int IList.Add (object value) 3047 { 3048 throw new NotSupportedException (); 3049 } 3050 IList.Clear()3051 void IList.Clear () 3052 { 3053 throw new NotSupportedException (); 3054 } 3055 IList.Insert(int index, object value)3056 void IList.Insert (int index, object value) 3057 { 3058 throw new NotSupportedException (); 3059 } 3060 IList.Remove(object value)3061 void IList.Remove (object value) 3062 { 3063 throw new NotSupportedException (); 3064 } 3065 IList.RemoveAt(int index)3066 void IList.RemoveAt (int index) 3067 { 3068 throw new NotSupportedException (); 3069 } 3070 IndexOf(object selectedObject)3071 public int IndexOf (object selectedObject) 3072 { 3073 int idx = owner.items.IndexOf (selectedObject); 3074 return idx == -1 ? -1 : owner.selected_indices.IndexOf (idx); 3075 } 3076 GetEnumerator()3077 public IEnumerator GetEnumerator () 3078 { 3079 //FIXME: write an enumerator that uses selection.GetEnumerator 3080 // so that invalidation is write on selection changes 3081 object [] items = new object [Count]; 3082 for (int i = 0; i < Count; i++) { 3083 items [i] = owner.items [owner.selected_indices [i]]; 3084 } 3085 3086 return items.GetEnumerator (); 3087 } 3088 3089 #endregion Public Methods 3090 } 3091 } 3092 } 3093