Code:
/ Dotnetfx_Vista_SP2 / Dotnetfx_Vista_SP2 / 8.0.50727.4016 / DEVDIV / depot / DevDiv / releases / whidbey / NetFxQFE / ndp / fx / src / WinForms / Managed / System / WinForms / ListBox.cs / 1 / ListBox.cs
//------------------------------------------------------------------------------ //// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- /* */ namespace System.Windows.Forms { using System.Runtime.Serialization.Formatters; using System.Runtime.Remoting; using System.Runtime.InteropServices; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System; using System.Security; using System.Security.Permissions; using System.Globalization; using System.Windows.Forms.Layout; using System.Drawing.Design; using System.ComponentModel; using System.Windows.Forms.ComponentModel; using System.Windows.Forms.VisualStyles; using System.Collections; using System.Drawing; using Microsoft.Win32; using System.Text; ////// /// /// This is a control that presents a list of items to the user. They may be /// navigated using the keyboard, or the scrollbar on the right side of the /// control. One or more items may be selected as well. /// [ ComVisible(true), ClassInterface(ClassInterfaceType.AutoDispatch), Designer("System.Windows.Forms.Design.ListBoxDesigner, " + AssemblyRef.SystemDesign), DefaultEvent("SelectedIndexChanged"), DefaultProperty("Items"), DefaultBindingProperty("SelectedValue"), SRDescription(SR.DescriptionListBox) ] public class ListBox : ListControl { ////// /// The preferred way to add items is to set them all via an array at once, /// which is definitely the most efficient way. The following is an example /// of this: /// ////// ListBox lb = new ListBox(); /// // set up properties on the listbox here. /// lb.Items.All = new String [] { /// "A", /// "B", /// "C", /// "D"}; ///
////// /// while doing a search, if no matches are found, this is returned /// public const int NoMatches = NativeMethods.LB_ERR; ////// /// The default item height for an owner-draw ListBox. /// public const int DefaultItemHeight = 13; // 13 == listbox's non-ownerdraw item height. That's with Win2k and // the default font; on other platforms and with other fonts, it may be different. private const int maxWin9xHeight = 32767; //Win9x doesn't deal with height > 32K private static readonly object EVENT_SELECTEDINDEXCHANGED = new object(); private static readonly object EVENT_DRAWITEM = new object(); private static readonly object EVENT_MEASUREITEM = new object(); static bool checkedOS = false; static bool runningOnWin2K = true; SelectedObjectCollection selectedItems; SelectedIndexCollection selectedIndices; ObjectCollection itemsCollection; // int itemHeight = DefaultItemHeight; int columnWidth; int requestedHeight; int topIndex; int horizontalExtent = 0; int maxWidth = -1; int updateCount = 0; bool sorted = false; bool scrollAlwaysVisible = false; bool integralHeight = true; bool integralHeightAdjust = false; bool multiColumn = false; bool horizontalScrollbar = false; bool useTabStops = true; bool useCustomTabOffsets = false; bool fontIsChanged = false; bool doubleClickFired = false; bool selectedValueChangedFired = false; DrawMode drawMode = System.Windows.Forms.DrawMode.Normal; BorderStyle borderStyle = System.Windows.Forms.BorderStyle.Fixed3D; SelectionMode selectionMode = System.Windows.Forms.SelectionMode.One; // VsWhidbey : 447524 SelectionMode cachedSelectionMode = System.Windows.Forms.SelectionMode.One; //We need to know that we are in middle of handleRecreate through Setter of SelectionMode. //In this case we set a bool denoting that we are changing SelectionMode and //in this case we should always use the cachedValue instead of the currently set value. //We need to change this in the count as well as SelectedIndex code where we access the SelectionMode. private bool selectionModeChanging = false; ////// This value stores the array of custom tabstops in the listbox. the array should be populated by /// integers in a ascending order. /// private IntegerCollection customTabOffsets; ////// /// Creates a basic win32 list box with default values for everything. /// public ListBox() : base() { SetStyle(ControlStyles.UserPaint | ControlStyles.StandardClick | ControlStyles.UseTextForAccessibility, false); // this class overrides GetPreferredSizeCore, let Control automatically cache the result SetState2(STATE2_USEPREFERREDSIZECACHE, true); SetBounds(0, 0, 120, 96); requestedHeight = Height; } ///public override Color BackColor { get { if (ShouldSerializeBackColor()) { return base.BackColor; } else { return SystemColors.Window; } } set { base.BackColor = value; } } /// /// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public override Image BackgroundImage { get { return base.BackgroundImage; } set { base.BackgroundImage = value; } } ///[To be supplied.] ////// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] new public event EventHandler BackgroundImageChanged { add { base.BackgroundImageChanged += value; } remove { base.BackgroundImageChanged -= value; } } /// /// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public override ImageLayout BackgroundImageLayout { get { return base.BackgroundImageLayout; } set { base.BackgroundImageLayout = value; } } ///[To be supplied.] ////// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] new public event EventHandler BackgroundImageLayoutChanged { add { base.BackgroundImageLayoutChanged += value; } remove { base.BackgroundImageLayoutChanged -= value; } } /// /// /// Retrieves the current border style. Values for this are taken from /// The System.Windows.Forms.BorderStyle enumeration. /// [ SRCategory(SR.CatAppearance), DefaultValue(BorderStyle.Fixed3D), DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE), SRDescription(SR.ListBoxBorderDescr) ] public BorderStyle BorderStyle { get { return borderStyle; } set { //valid values are 0x0 to 0x2 if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)){ throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle)); } if (value != borderStyle) { borderStyle = value; RecreateHandle(); // Avoid the listbox and textbox behavior in Collection editors // integralHeightAdjust = true; try { Height = requestedHeight; } finally { integralHeightAdjust = false; } } } } ////// /// [ SRCategory(SR.CatBehavior), Localizable(true), DefaultValue(0), SRDescription(SR.ListBoxColumnWidthDescr) ] public int ColumnWidth { get { return columnWidth; } set { if (value < 0) { throw new ArgumentException(SR.GetString(SR.InvalidLowBoundArgumentEx, "value", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); } if (columnWidth != value) { columnWidth = value; // if it's zero, we need to reset, and only way to do // that is to recreate the handle. if (columnWidth == 0) { RecreateHandle(); } else if (IsHandleCreated) { SendMessage(NativeMethods.LB_SETCOLUMNWIDTH, columnWidth, 0); } } } } ////// /// Retrieves the parameters needed to create the handle. Inheriting classes /// can override this to provide extra functionality. They should not, /// however, forget to call base.getCreateParams() first to get the struct /// filled up with the basic info. /// ///protected override CreateParams CreateParams { [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] get { CreateParams cp = base.CreateParams; cp.ClassName = "LISTBOX"; cp.Style |= NativeMethods.WS_VSCROLL | NativeMethods.LBS_NOTIFY | NativeMethods.LBS_HASSTRINGS; if (scrollAlwaysVisible) cp.Style |= NativeMethods.LBS_DISABLENOSCROLL; if (!integralHeight) cp.Style |= NativeMethods.LBS_NOINTEGRALHEIGHT; if (useTabStops) cp.Style |= NativeMethods.LBS_USETABSTOPS; switch (borderStyle) { case BorderStyle.Fixed3D: cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; break; case BorderStyle.FixedSingle: cp.Style |= NativeMethods.WS_BORDER; break; } if (multiColumn) { cp.Style |= NativeMethods.LBS_MULTICOLUMN | NativeMethods.WS_HSCROLL; } else if (horizontalScrollbar) { cp.Style |= NativeMethods.WS_HSCROLL; } switch (selectionMode) { case SelectionMode.None: cp.Style |= NativeMethods.LBS_NOSEL; break; case SelectionMode.MultiSimple: cp.Style |= NativeMethods.LBS_MULTIPLESEL; break; case SelectionMode.MultiExtended: cp.Style |= NativeMethods.LBS_EXTENDEDSEL; break; case SelectionMode.One: break; } switch (drawMode) { case DrawMode.Normal: break; case DrawMode.OwnerDrawFixed: cp.Style |= NativeMethods.LBS_OWNERDRAWFIXED; break; case DrawMode.OwnerDrawVariable: cp.Style |= NativeMethods.LBS_OWNERDRAWVARIABLE; break; } return cp; } } /// /// /// Enables a list box to recognize and expand tab characters when drawing /// its strings using the CustomTabOffsets integer array. /// [ SRCategory(SR.CatBehavior), DefaultValue(false), Browsable(false) ] public bool UseCustomTabOffsets { get { return useCustomTabOffsets; } set { if (useCustomTabOffsets != value) { useCustomTabOffsets = value; RecreateHandle(); } } } ///protected override Size DefaultSize { get { return new Size(120, 96); } } /// /// /// Retrieves the style of the listbox. This will indicate if the system /// draws it, or if the user paints each item manually. It also indicates /// whether or not items have to be of the same height. /// [ SRCategory(SR.CatBehavior), DefaultValue(DrawMode.Normal), SRDescription(SR.ListBoxDrawModeDescr), RefreshProperties(RefreshProperties.Repaint) ] public virtual DrawMode DrawMode { get { return drawMode; } set { //valid values are 0x0 to 0x2 if (!ClientUtils.IsEnumValid(value, (int)value, (int)DrawMode.Normal, (int)DrawMode.OwnerDrawVariable)) { throw new InvalidEnumArgumentException("value", (int)value, typeof(DrawMode)); } if (drawMode != value) { if (MultiColumn && value == DrawMode.OwnerDrawVariable) { throw new ArgumentException(SR.GetString(SR.ListBoxVarHeightMultiCol), "value"); } drawMode = value; RecreateHandle(); if (drawMode == DrawMode.OwnerDrawVariable) { // VSWhidbey 139179 - force a layout after RecreateHandle() completes because now // the LB is definitely fully populated and can report a preferred size accurately. LayoutTransaction.DoLayoutIf(AutoSize, this.ParentInternal, this, PropertyNames.DrawMode); } } } } // Used internally to find the currently focused item // internal int FocusedIndex { get { if (IsHandleCreated) { return (int)SendMessage(NativeMethods.LB_GETCARETINDEX, 0, 0); } return -1; } } ///// VSWhidbey 95179: The scroll bars don't display properly when the IntegralHeight == false // and the control is resized before the font size is change and the new font size causes // the height of all the items to exceed the new height of the control. This is a bug in // the control, but can be easily worked around by removing and re-adding all the items. public override Font Font { get { return base.Font; } set { base.Font = value; if (false == integralHeight) { // VSWhidbey 95179: Refresh the list to force the scroll bars to display // when the integral height is false. RefreshItems(); } } } /// public override Color ForeColor { get { if (ShouldSerializeForeColor()) { return base.ForeColor; } else { return SystemColors.WindowText; } } set { base.ForeColor = value; } } /// /// /// Indicates the width, in pixels, by which a list box can be scrolled horizontally (the scrollable width). /// This property will only have an effect if HorizontalScrollbars is true. /// [ SRCategory(SR.CatBehavior), DefaultValue(0), Localizable(true), SRDescription(SR.ListBoxHorizontalExtentDescr) ] public int HorizontalExtent { get { return horizontalExtent; } set { if (value != horizontalExtent) { horizontalExtent = value; UpdateHorizontalExtent(); } } } ////// /// Indicates whether or not the ListBox should display a horizontal scrollbar /// when the items extend beyond the right edge of the ListBox. /// If true, the scrollbar will automatically set its extent depending on the length /// of items in the ListBox. The exception is if the ListBox is owner-draw, in /// which case HorizontalExtent will need to be explicitly set. /// [ SRCategory(SR.CatBehavior), DefaultValue(false), Localizable(true), SRDescription(SR.ListBoxHorizontalScrollbarDescr) ] public bool HorizontalScrollbar { get { return horizontalScrollbar; } set { if (value != horizontalScrollbar) { horizontalScrollbar = value; // There seems to be a bug in the native ListBox in that the addition // of the horizontal scroll bar does not get reflected in the control // rightaway. So, we refresh the items here. RefreshItems(); // Only need to recreate the handle if not MultiColumn // (HorizontalScrollbar has no effect on a MultiColumn listbox) // if (!MultiColumn) { RecreateHandle(); } } } } ////// /// Indicates if the listbox should avoid showing partial Items. If so, /// then only full items will be displayed, and the listbox will be resized /// to prevent partial items from being shown. Otherwise, they will be /// shown /// [ SRCategory(SR.CatBehavior), DefaultValue(true), Localizable(true), SRDescription(SR.ListBoxIntegralHeightDescr), RefreshProperties(RefreshProperties.Repaint) ] public bool IntegralHeight { get { return integralHeight; } set { if (integralHeight != value) { integralHeight = value; RecreateHandle(); // Avoid the listbox and textbox behaviour in Collection editors // integralHeightAdjust = true; try { Height = requestedHeight; } finally { integralHeightAdjust = false; } } } } ////// /// [ SRCategory(SR.CatBehavior), DefaultValue(DefaultItemHeight), Localizable(true), SRDescription(SR.ListBoxItemHeightDescr), RefreshProperties(RefreshProperties.Repaint) ] public virtual int ItemHeight { get { if (drawMode == DrawMode.OwnerDrawFixed || drawMode == DrawMode.OwnerDrawVariable) { return itemHeight; } return GetItemHeight(0); } set { if (value < 1 || value > 255) { throw new ArgumentOutOfRangeException("ItemHeight", SR.GetString(SR.InvalidExBoundArgument, "ItemHeight", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture), "256")); } if (itemHeight != value) { itemHeight = value; if (drawMode == DrawMode.OwnerDrawFixed && IsHandleCreated) { BeginUpdate(); SendMessage(NativeMethods.LB_SETITEMHEIGHT, 0, value); // Changing the item height might require a resize for IntegralHeight list boxes // if (IntegralHeight) { Size oldSize = Size; Size = new Size(oldSize.Width + 1, oldSize.Height); Size = oldSize; } EndUpdate(); } } } } ////// Returns /// the height of an item in an owner-draw list box. ////// /// Collection of items in this listbox. /// [ SRCategory(SR.CatData), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), Localizable(true), SRDescription(SR.ListBoxItemsDescr), Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), MergableProperty(false) ] public ObjectCollection Items { get { if (itemsCollection == null) { itemsCollection = CreateItemCollection(); } return itemsCollection; } } // Computes the maximum width of all items in the ListBox // internal virtual int MaxItemWidth { get { if (horizontalExtent > 0) { return horizontalExtent; } if (DrawMode != DrawMode.Normal) { return -1; } // Return cached maxWidth if available // if (maxWidth > -1) { return maxWidth; } // Compute maximum width // maxWidth = ComputeMaxItemWidth(maxWidth); return maxWidth; } } ////// /// [ SRCategory(SR.CatBehavior), DefaultValue(false), SRDescription(SR.ListBoxMultiColumnDescr) ] public bool MultiColumn { get { return multiColumn; } set { if (multiColumn != value) { if (value && drawMode == DrawMode.OwnerDrawVariable) { throw new ArgumentException(SR.GetString(SR.ListBoxVarHeightMultiCol), "value"); } multiColumn = value; RecreateHandle(); } } } ////// Indicates if the listbox is multi-column /// or not. ////// /// The total height of the items in the list box. /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.ListBoxPreferredHeightDescr) ] public int PreferredHeight { get { int height = 0; if (drawMode == DrawMode.OwnerDrawVariable) { // VSWhidbey 139179 - don't try to get item heights from the LB when items haven't been // added to the LB yet. Just return current height. if (RecreatingHandle || GetState(STATE_CREATINGHANDLE)) { height = this.Height; } else { if (itemsCollection != null) { int cnt = itemsCollection.Count; for (int i = 0; i < cnt; i++) { height += GetItemHeight(i); } } } } else { //VSWhidbey #148270 //When the list is empty, we don't want to multiply by 0 here. int cnt = (itemsCollection == null || itemsCollection.Count == 0) ? 1 : itemsCollection.Count; height = GetItemHeight(0) * cnt; } if (borderStyle != BorderStyle.None) { height += SystemInformation.BorderSize.Height * 4 + 3; } return height; } } internal override Size GetPreferredSizeCore(Size proposedConstraints) { int height = PreferredHeight; int width; // Convert with a dummy height to add space required for borders // VSWhidbey #151141 -PreferredSize should return either the new // size of the control, or the default size if the handle has not been // created if (IsHandleCreated) { width = SizeFromClientSize(new Size(MaxItemWidth, height)).Width; width += SystemInformation.VerticalScrollBarWidth + 4; } else { return DefaultSize; } return new Size(width, height) + Padding.Size; } ///public override RightToLeft RightToLeft { get { if (!RunningOnWin2K) { return RightToLeft.No; } return base.RightToLeft; } set { base.RightToLeft = value; } } static bool RunningOnWin2K { get { if (!checkedOS) { if (Environment.OSVersion.Platform != System.PlatformID.Win32NT || Environment.OSVersion.Version.Major < 5) { runningOnWin2K = false; checkedOS = true; } } return runningOnWin2K; } } /// /// /// [ SRCategory(SR.CatBehavior), DefaultValue(false), Localizable(true), SRDescription(SR.ListBoxScrollIsVisibleDescr) ] public bool ScrollAlwaysVisible { get { return scrollAlwaysVisible; } set { if (scrollAlwaysVisible != value) { scrollAlwaysVisible = value; RecreateHandle(); } } } ////// Gets or sets whether the scrollbar is shown at all times. ////// /// Indicates whether list currently allows selection of list items. /// For ListBox, this returns true unless SelectionMode is SelectionMode.None. /// protected override bool AllowSelection { get { return selectionMode != SelectionMode.None; } } ////// /// The index of the currently selected item in the list, if there /// is one. If the value is -1, there is currently no selection. If the /// value is 0 or greater, than the value is the index of the currently /// selected item. If the MultiSelect property on the ListBox is true, /// then a non-zero value for this property is the index of the first /// selection /// [ Browsable(false), Bindable(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.ListBoxSelectedIndexDescr) ] public override int SelectedIndex { get { SelectionMode current = (selectionModeChanging) ? cachedSelectionMode : selectionMode; if (current == SelectionMode.None) { return -1; } if (current == SelectionMode.One && IsHandleCreated) { return (int)SendMessage(NativeMethods.LB_GETCURSEL, 0, 0); } if (itemsCollection != null && SelectedItems.Count > 0) { return Items.IndexOfIdentifier(SelectedItems.GetObjectAt(0)); } return -1; } set { int itemCount = (itemsCollection == null) ? 0 : itemsCollection.Count; if (value < -1 || value >= itemCount) { throw new ArgumentOutOfRangeException("SelectedIndex", SR.GetString(SR.InvalidArgument, "SelectedIndex", (value).ToString(CultureInfo.CurrentCulture))); } if (selectionMode == SelectionMode.None) { throw new ArgumentException(SR.GetString(SR.ListBoxInvalidSelectionMode), "SelectedIndex"); } if (selectionMode == SelectionMode.One && value != -1) { // Single select an individual value. int currentIndex = SelectedIndex; if (currentIndex != value) { if (currentIndex != -1) { SelectedItems.SetSelected(currentIndex, false); } SelectedItems.SetSelected(value, true); if (IsHandleCreated) { NativeSetSelected(value, true); } OnSelectedIndexChanged(EventArgs.Empty); } } else if (value == -1) { if (SelectedIndex != -1) { ClearSelected(); // ClearSelected raises OnSelectedIndexChanged for us } } else { if (!SelectedItems.GetSelected(value)) { // Select this item while keeping any previously selected items selected. // SelectedItems.SetSelected(value, true); if (IsHandleCreated) { NativeSetSelected(value, true); } OnSelectedIndexChanged(EventArgs.Empty); } } } } ////// /// A collection of the indices of the selected items in the /// list box. If there are no selected items in the list box, the result is /// an empty collection. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.ListBoxSelectedIndicesDescr) ] public SelectedIndexCollection SelectedIndices { get { if (selectedIndices == null) { selectedIndices = new SelectedIndexCollection(this); } return selectedIndices; } } ////// /// The value of the currently selected item in the list, if there /// is one. If the value is null, there is currently no selection. If the /// value is non-null, then the value is that of the currently selected /// item. If the MultiSelect property on the ListBox is true, then a /// non-null return value for this method is the value of the first item /// selected /// [ Browsable(false), Bindable(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.ListBoxSelectedItemDescr) ] public object SelectedItem { get { if (SelectedItems.Count > 0) { return SelectedItems[0]; } return null; } set { if (itemsCollection != null) { if (value != null) { int index = itemsCollection.IndexOf(value); if (index != -1) { SelectedIndex = index; } } else { SelectedIndex = -1; } } } } ////// /// The collection of selected items. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.ListBoxSelectedItemsDescr) ] public SelectedObjectCollection SelectedItems { get { if (selectedItems == null) { selectedItems = new SelectedObjectCollection(this); } return selectedItems; } } ////// /// Controls how many items at a time can be selected in the listbox. Valid /// values are from the System.Windows.Forms.SelectionMode enumeration. /// [ SRCategory(SR.CatBehavior), DefaultValue(SelectionMode.One), SRDescription(SR.ListBoxSelectionModeDescr) ] public virtual SelectionMode SelectionMode { get { return selectionMode; } set { if (!ClientUtils.IsEnumValid(value, (int)value, (int)SelectionMode.None, (int)SelectionMode.MultiExtended)) { throw new InvalidEnumArgumentException("value", (int)value, typeof(SelectionMode)); } if (selectionMode != value) { SelectedItems.EnsureUpToDate(); selectionMode = value; try { selectionModeChanging = true; RecreateHandle(); } finally { selectionModeChanging = false; cachedSelectionMode = selectionMode; // update the selectedItems list and SelectedItems index collection if (IsHandleCreated) { NativeUpdateSelection(); } } } } } ////// /// Indicates if the ListBox is sorted or not. 'true' means that strings in /// the list will be sorted alphabetically /// [ SRCategory(SR.CatBehavior), DefaultValue(false), SRDescription(SR.ListBoxSortedDescr) ] public bool Sorted { get { return sorted; } set { if (sorted != value) { sorted = value; if (sorted && itemsCollection != null && itemsCollection.Count >= 1) { Sort(); } } } } ////// /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Bindable(false) ] public override string Text { get { if (SelectionMode != SelectionMode.None && SelectedItem != null) { if (FormattingEnabled) { return GetItemText(SelectedItem); } else { return FilterItemOnProperty(SelectedItem).ToString(); } } else { return base.Text; } } set { base.Text = value; // Scan through the list items looking for the supplied text string. If we find it, // select it. // if (SelectionMode != SelectionMode.None && value != null && (SelectedItem == null || !value.Equals(GetItemText(SelectedItem)))) { int cnt = Items.Count; for (int index=0; index < cnt; ++index) { if (String.Compare(value, GetItemText(Items[index]), true, CultureInfo.CurrentCulture) == 0) { SelectedIndex = index; return; } } } } } ///[To be supplied.] ////// [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] new public event EventHandler TextChanged { add { base.TextChanged += value; } remove { base.TextChanged -= value; } } /// /// /// The index of the first visible item in a list box. Initially /// the item with index 0 is at the top of the list box, but if the list /// box contents have been scrolled another item may be at the top. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.ListBoxTopIndexDescr) ] public int TopIndex { get { if (IsHandleCreated) { return (int)SendMessage(NativeMethods.LB_GETTOPINDEX, 0, 0); } else { return topIndex; } } set { if (IsHandleCreated) { SendMessage(NativeMethods.LB_SETTOPINDEX, value, 0); } else { topIndex = value; } } } ////// /// Enables a list box to recognize and expand tab characters when drawing /// its strings. /// [ SRCategory(SR.CatBehavior), DefaultValue(true), SRDescription(SR.ListBoxUseTabStopsDescr) ] public bool UseTabStops { get { return useTabStops; } set { if (useTabStops != value) { useTabStops = value; RecreateHandle(); } } } ////// /// Allows to set the width of the tabs between the items in the list box. /// The integer array should have the tab spaces in the ascending order. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.ListBoxCustomTabOffsetsDescr), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), Browsable(false) ] public IntegerCollection CustomTabOffsets { get { if (customTabOffsets == null) { customTabOffsets = new IntegerCollection(this); } return customTabOffsets; } } ////// /// Performs the work of adding the specified items to the Listbox /// [Obsolete("This method has been deprecated. There is no replacement. http://go.microsoft.com/fwlink/?linkid=14202")] protected virtual void AddItemsCore(object[] value) { int count = value == null? 0: value.Length; if (count == 0) { return; } Items.AddRangeInternal(value); } ///[Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] public new event EventHandler Click { add { base.Click += value; } remove { base.Click -= value; } } /// [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] public new event MouseEventHandler MouseClick { add { base.MouseClick += value; } remove { base.MouseClick -= value; } } /// /// /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public new Padding Padding { get { return base.Padding; } set { base.Padding = value;} } [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never) ] public new event EventHandler PaddingChanged { add { base.PaddingChanged += value; } remove { base.PaddingChanged -= value; } } ////// ///[To be supplied.] ////// /// ListBox / CheckedListBox Onpaint. /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public new event PaintEventHandler Paint { add { base.Paint += value; } remove { base.Paint -= value; } } ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.drawItemEventDescr)] public event DrawItemEventHandler DrawItem { add { Events.AddHandler(EVENT_DRAWITEM, value); } remove { Events.RemoveHandler(EVENT_DRAWITEM, value); } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.measureItemEventDescr)] public event MeasureItemEventHandler MeasureItem { add { Events.AddHandler(EVENT_MEASUREITEM, value); } remove { Events.RemoveHandler(EVENT_MEASUREITEM, value); } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.selectedIndexChangedEventDescr)] public event EventHandler SelectedIndexChanged { add { Events.AddHandler(EVENT_SELECTEDINDEXCHANGED, value); } remove { Events.RemoveHandler(EVENT_SELECTEDINDEXCHANGED, value); } } ///[To be supplied.] ////// /// While the preferred way to insert items is to set Items.All, /// and set all the items at once, there are times when you may wish to /// insert each item one at a time. To help with the performance of this, /// it is desirable to prevent the ListBox from painting during these /// operations. This method, along with EndUpdate, is the preferred /// way of doing this. Don't forget to call EndUpdate when you're done, /// or else the ListBox won't paint properly afterwards. /// public void BeginUpdate() { BeginUpdateInternal(); updateCount++; } private void CheckIndex(int index) { if (index < 0 || index >= Items.Count) throw new ArgumentOutOfRangeException("index", SR.GetString(SR.IndexOutOfRange, index.ToString(CultureInfo.CurrentCulture))); } private void CheckNoDataSource() { if (DataSource != null) throw new ArgumentException(SR.GetString(SR.DataSourceLocksItems)); } ////// /// protected virtual ObjectCollection CreateItemCollection() { return new ObjectCollection(this); } internal virtual int ComputeMaxItemWidth(int oldMax) { // pass LayoutUtils the collection of strings string[] strings = new string[this.Items.Count]; for (int i = 0; i < Items.Count; i ++) { strings[i] = GetItemText(Items[i]); } Size textSize = LayoutUtils.OldGetLargestStringSizeInCollection(Font, strings); return Math.Max(oldMax, textSize.Width); } ///[To be supplied.] ////// /// Unselects all currently selected items. /// public void ClearSelected() { bool hadSelection = false; int itemCount = (itemsCollection == null) ? 0 : itemsCollection.Count; for (int x = 0; x < itemCount;x++) { if (SelectedItems.GetSelected(x)) { hadSelection = true; SelectedItems.SetSelected(x, false); if (IsHandleCreated) { NativeSetSelected(x, false); } } } if (hadSelection) { OnSelectedIndexChanged(EventArgs.Empty); } } ////// /// While the preferred way to insert items is to set Items.All, /// and set all the items at once, there are times when you may wish to /// insert each item one at a time. To help with the performance of this, /// it is desirable to prevent the ListBox from painting during these /// operations. This method, along with BeginUpdate, is the preferred /// way of doing this. BeginUpdate should be called first, and this method /// should be called when you want the control to start painting again. /// public void EndUpdate() { EndUpdateInternal(); --updateCount; } ////// /// Finds the first item in the list box that starts with the given string. /// The search is not case sensitive. /// public int FindString(string s) { return FindString(s, -1); } ////// /// Finds the first item after the given index which starts with the given /// string. The search is not case sensitive. /// public int FindString(string s, int startIndex) { if (s == null) return -1; int itemCount = (itemsCollection == null) ? 0 : itemsCollection.Count; if (itemCount == 0) { return -1; } // VSWhidbey 95158: The last item in the list is still a valid starting point for a search. if (startIndex < -1 || startIndex >= itemCount) { throw new ArgumentOutOfRangeException("startIndex"); } // Always use the managed FindStringInternal instead of LB_FINDSTRING. // The managed version correctly handles Turkish I. return FindStringInternal(s, Items, startIndex, false); } ////// /// Finds the first item in the list box that matches the given string. /// The strings must match exactly, except for differences in casing. /// public int FindStringExact(string s) { return FindStringExact(s, -1); } ////// /// Finds the first item after the given index that matches the given /// string. The strings must match excatly, except for differences in /// casing. /// public int FindStringExact(string s, int startIndex) { if (s == null) return -1; int itemCount = (itemsCollection == null) ? 0 : itemsCollection.Count; if (itemCount == 0) { return -1; } // VSWhidbey 95158: The last item in the list is still a valid starting point for a search. if (startIndex < -1 || startIndex >= itemCount) { throw new ArgumentOutOfRangeException("startIndex"); } // Always use the managed FindStringInternal instead of LB_FINDSTRING. // The managed version correctly handles Turkish I. // return FindStringInternal(s, Items, startIndex, true); } ////// /// Returns the height of the given item in a list box. The index parameter /// is ignored if drawMode is not OwnerDrawVariable. /// public int GetItemHeight(int index) { int itemCount = (itemsCollection == null) ? 0 : itemsCollection.Count; // Note: index == 0 is OK even if the ListBox currently has // no items. // if (index < 0 || (index > 0 && index >= itemCount)) throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); if (drawMode != DrawMode.OwnerDrawVariable) index = 0; if (IsHandleCreated) { int h = (int)SendMessage(NativeMethods.LB_GETITEMHEIGHT, index, 0); if (h == -1) throw new Win32Exception(); return h; } return itemHeight; } ////// /// Retrieves a Rectangle object which describes the bounding rectangle /// around an item in the list. If the item in question is not visible, /// the rectangle will be outside the visible portion of the control. /// public Rectangle GetItemRectangle(int index) { CheckIndex(index); NativeMethods.RECT rect = new NativeMethods.RECT(); SendMessage(NativeMethods.LB_GETITEMRECT, index, ref rect); return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom); } ////// List box overrides GetScaledBounds to ensure we always scale the requested /// height, not the current height. /// [EditorBrowsable(EditorBrowsableState.Advanced)] protected override Rectangle GetScaledBounds(Rectangle bounds, SizeF factor, BoundsSpecified specified) { // update bounds' height to use the requested height, not the current height. These // can be different if integral height is turned on. bounds.Height = requestedHeight; return base.GetScaledBounds(bounds, factor, specified); } ////// /// Tells you whether or not the item at the supplied index is selected /// or not. /// public bool GetSelected(int index) { CheckIndex(index); return GetSelectedInternal(index); } private bool GetSelectedInternal(int index) { if (IsHandleCreated) { int sel = (int)SendMessage(NativeMethods.LB_GETSEL, index, 0); if (sel == -1) { throw new Win32Exception(); } return sel > 0; } else { if (itemsCollection != null && SelectedItems.GetSelected(index)) { return true; } return false; } } ////// /// Retrieves the index of the item at the given coordinates. /// public int IndexFromPoint(Point p) { return IndexFromPoint(p.X, p.Y); } ////// /// Retrieves the index of the item at the given coordinates. /// public int IndexFromPoint(int x, int y) { //NT4 SP6A : SendMessage Fails. So First check whether the point is in Client Co-ordinates and then //call Sendmessage. // NativeMethods.RECT r = new NativeMethods.RECT(); UnsafeNativeMethods.GetClientRect(new HandleRef(this, Handle), ref r); if (r.left <= x && x < r.right && r.top <= y && y < r.bottom) { int index = (int)SendMessage(NativeMethods.LB_ITEMFROMPOINT, 0, (int)NativeMethods.Util.MAKELPARAM(x, y)); if (NativeMethods.Util.HIWORD(index) == 0) { // Inside ListBox client area return NativeMethods.Util.LOWORD(index); } } return NoMatches; } ////// Adds the given item to the native combo box. This asserts if the handle hasn't been /// created. /// private int NativeAdd(object item) { Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); int insertIndex = (int)SendMessage(NativeMethods.LB_ADDSTRING, 0, GetItemText(item)); if (insertIndex == NativeMethods.LB_ERRSPACE) { throw new OutOfMemoryException(); } if (insertIndex == NativeMethods.LB_ERR) { // On some platforms (e.g. Win98), the ListBox control // appears to return LB_ERR if there are a large number (>32000) // of items. It doesn't appear to set error codes appropriately, // so we'll have to assume that LB_ERR corresponds to item // overflow. // throw new OutOfMemoryException(SR.GetString(SR.ListBoxItemOverflow)); } return insertIndex; } ////// Clears the contents of the combo box. /// private void NativeClear() { Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); SendMessage(NativeMethods.LB_RESETCONTENT, 0, 0); } ////// Get the text stored by the native control for the specified list item. /// internal string NativeGetItemText(int index) { int len = (int) SendMessage(NativeMethods.LB_GETTEXTLEN, index, 0); StringBuilder sb = new StringBuilder(len + 1); UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LB_GETTEXT, index, sb); return sb.ToString(); } ////// Inserts the given item to the native combo box at the index. This asserts if the handle hasn't been /// created or if the resulting insert index doesn't match the passed in index. /// private int NativeInsert(int index, object item) { Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); int insertIndex = (int)SendMessage(NativeMethods.LB_INSERTSTRING, index, GetItemText(item)); if (insertIndex == NativeMethods.LB_ERRSPACE) { throw new OutOfMemoryException(); } if (insertIndex == NativeMethods.LB_ERR) { // On some platforms (e.g. Win98), the ListBox control // appears to return LB_ERR if there are a large number (>32000) // of items. It doesn't appear to set error codes appropriately, // so we'll have to assume that LB_ERR corresponds to item // overflow. // throw new OutOfMemoryException(SR.GetString(SR.ListBoxItemOverflow)); } Debug.Assert(insertIndex == index, "NativeListBox inserted at " + insertIndex + " not the requested index of " + index); return insertIndex; } ////// Removes the native item from the given index. /// private void NativeRemoveAt(int index) { Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); bool selected = ((int)SendMessage(NativeMethods.LB_GETSEL, (IntPtr)index, IntPtr.Zero) > 0); SendMessage(NativeMethods.LB_DELETESTRING, index, 0); //If the item currently selected is removed then we should fire a Selectionchanged event... //as the next time selected index returns -1... if (selected) { OnSelectedIndexChanged(EventArgs.Empty); } } ////// Sets the selection of the given index to the native window. This does not change /// the collection; you must update the collection yourself. /// private void NativeSetSelected(int index, bool value) { Debug.Assert(IsHandleCreated, "Should only call Native methods after the handle has been created"); Debug.Assert(selectionMode != SelectionMode.None, "Guard against setting selection for None selection mode outside this code."); if (selectionMode == SelectionMode.One) { SendMessage(NativeMethods.LB_SETCURSEL, (value ? index : -1), 0); } else { SendMessage(NativeMethods.LB_SETSEL, value? -1: 0, index); } } ////// This is called by the SelectedObjectCollection in response to the first /// query on that collection after we have called Dirty(). Dirty() is called /// when we receive a LBN_SELCHANGE message. /// private void NativeUpdateSelection() { Debug.Assert(IsHandleCreated, "Should only call native methods if handle is created"); // Clear the selection state. // int cnt = Items.Count; for (int i = 0; i < cnt; i++) { SelectedItems.SetSelected(i, false); } int[] result = null; switch (selectionMode) { case SelectionMode.One: int index = (int)SendMessage(NativeMethods.LB_GETCURSEL, 0, 0); if (index >= 0) result = new int[] {index}; break; case SelectionMode.MultiSimple: case SelectionMode.MultiExtended: int count = (int)SendMessage(NativeMethods.LB_GETSELCOUNT, 0, 0); if (count > 0) { result = new int[count]; UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LB_GETSELITEMS, count, result); } break; } // Now set the selected state on the appropriate items. // if (result != null) { foreach(int i in result) { SelectedItems.SetSelected(i, true); } } } ////// /// protected override void OnChangeUICues(UICuesEventArgs e) { // ListBox seems to get a bit confused when the UI cues change for the first // time - it draws the focus rect when it shouldn't and vice-versa. So when // the UI cues change, we just do an extra invalidate to get it into the // right state. // Invalidate(); base.OnChangeUICues(e); } ///[To be supplied.] ////// /// Actually goes and fires the drawItem event. Inheriting controls /// should use this to know when the event is fired [this is preferable to /// adding an event handler yourself for this event]. They should, /// however, remember to call base.onDrawItem(e); to ensure the event is /// still fired to external listeners /// protected virtual void OnDrawItem(DrawItemEventArgs e) { DrawItemEventHandler handler = (DrawItemEventHandler)Events[EVENT_DRAWITEM]; if (handler != null) { handler(this, e); } } ////// /// We need to know when the window handle has been created so we can /// set up a few things, like column width, etc! Inheriting classes should /// not forget to call base.OnHandleCreated(). /// protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); //for getting the current Locale to set the Scrollbars... // SendMessage(NativeMethods.LB_SETLOCALE, CultureInfo.CurrentCulture.LCID, 0); if (columnWidth != 0) { SendMessage(NativeMethods.LB_SETCOLUMNWIDTH, columnWidth, 0); } if (drawMode == DrawMode.OwnerDrawFixed) { SendMessage(NativeMethods.LB_SETITEMHEIGHT, 0, ItemHeight); } if (topIndex != 0) { SendMessage(NativeMethods.LB_SETTOPINDEX, topIndex, 0); } if (UseCustomTabOffsets && CustomTabOffsets != null) { int wpar = CustomTabOffsets.Count; int[] offsets = new int[wpar]; CustomTabOffsets.CopyTo(offsets, 0); UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LB_SETTABSTOPS, wpar, offsets); } if (itemsCollection != null) { int count = itemsCollection.Count; for(int i = 0; i < count; i++) { NativeAdd(itemsCollection[i]); if (selectionMode != SelectionMode.None) { if (selectedItems != null) { selectedItems.PushSelectionIntoNativeListBox(i); } } } } if (selectedItems != null) { if (selectedItems.Count > 0 && selectionMode == SelectionMode.One) { SelectedItems.Dirty(); SelectedItems.EnsureUpToDate(); } } UpdateHorizontalExtent(); } ////// /// Overridden to make sure that we set up and clear out items /// correctly. Inheriting controls should not forget to call /// base.OnHandleDestroyed() /// protected override void OnHandleDestroyed(EventArgs e) { SelectedItems.EnsureUpToDate(); if (Disposing) { itemsCollection = null; } base.OnHandleDestroyed(e); } ////// /// protected virtual void OnMeasureItem(MeasureItemEventArgs e) { MeasureItemEventHandler handler = (MeasureItemEventHandler)Events[EVENT_MEASUREITEM]; if (handler != null) { handler(this, e); } } ///[To be supplied.] ////// /// protected override void OnFontChanged(EventArgs e) { base.OnFontChanged(e); // Changing the font causes us to resize, always rounding down. // Make sure we do this after base.OnPropertyChanged, which sends the WM_SETFONT message // Avoid the listbox and textbox behaviour in Collection editors // UpdateFontCache(); } ///[To be supplied.] ////// /// protected override void OnParentChanged(EventArgs e) { base.OnParentChanged(e); //No need to RecreateHandle if we are removing the Listbox from controls collection... //so check the parent before recreating the handle... if (this.ParentInternal != null) { RecreateHandle(); } } ///We override this so we can re-create the handle if the parent has changed. ////// /// protected override void OnResize(EventArgs e) { base.OnResize(e); // There are some repainting issues for RightToLeft - so invalidate when we resize. // if (RightToLeft == RightToLeft.Yes || this.HorizontalScrollbar) { Invalidate(); } } ///[To be supplied.] ////// /// Actually goes and fires the selectedIndexChanged event. Inheriting controls /// should use this to know when the event is fired [this is preferable to /// adding an event handler on yourself for this event]. They should, /// however, remember to call base.OnSelectedIndexChanged(e); to ensure the event is /// still fired to external listeners /// protected override void OnSelectedIndexChanged(EventArgs e) { base.OnSelectedIndexChanged(e); // set the position in the dataSource, if there is any // we will only set the position in the currencyManager if it is different // from the SelectedIndex. Setting CurrencyManager::Position (even w/o changing it) // calls CurrencyManager::EndCurrentEdit, and that will pull the dataFrom the controls // into the backEnd. We do not need to do that. // if (this.DataManager != null && DataManager.Position != SelectedIndex) { //read this as "if everett or (whidbey and selindex is valid)" if (!FormattingEnabled || this.SelectedIndex != -1) { // VSWhidbey 95176: don't change dataManager position if we simply unselected everything. // (Doing so would cause the first LB item to be selected...) this.DataManager.Position = this.SelectedIndex; } } // VSWhidbey 163411: Call the handler after updating the DataManager's position so that // the DataManager's selected index will be correct in an event handler. EventHandler handler = (EventHandler)Events[EVENT_SELECTEDINDEXCHANGED]; if (handler != null) { handler(this, e); } } ///protected override void OnSelectedValueChanged(EventArgs e) { base.OnSelectedValueChanged(e); selectedValueChangedFired = true; } /// protected override void OnDataSourceChanged(EventArgs e) { if (DataSource == null) { BeginUpdate(); SelectedIndex = -1; Items.ClearInternal(); EndUpdate(); } base.OnDataSourceChanged(e); RefreshItems(); } /// protected override void OnDisplayMemberChanged(EventArgs e) { base.OnDisplayMemberChanged(e); // we want to use the new DisplayMember even if there is no data source RefreshItems(); if (SelectionMode != SelectionMode.None && this.DataManager != null) this.SelectedIndex = this.DataManager.Position; } /// /// /// Forces the ListBox to invalidate and immediately /// repaint itself and any children if OwnerDrawVariable. /// public override void Refresh() { if (drawMode == DrawMode.OwnerDrawVariable) { //Fire MeasureItem for Each Item in the Listbox... int cnt = Items.Count; Graphics graphics = CreateGraphicsInternal(); try { for (int i = 0; i < cnt; i++) { MeasureItemEventArgs mie = new MeasureItemEventArgs(graphics, i, ItemHeight); OnMeasureItem(mie); } } finally { graphics.Dispose(); } } base.Refresh(); } ////// /// Reparses the objects, getting new text strings for them. /// ///protected override void RefreshItems() { // Store the currently selected object collection. // ObjectCollection savedItems = itemsCollection; // Clear the items. // itemsCollection = null; selectedIndices = null; if (IsHandleCreated) { NativeClear(); } object[] newItems = null; // if we have a dataSource and a DisplayMember, then use it // to populate the Items collection // if (this.DataManager != null && this.DataManager.Count != -1) { newItems = new object[this.DataManager.Count]; for(int i = 0; i < newItems.Length; i++) { newItems[i] = this.DataManager[i]; } } else if (savedItems != null) { newItems = new object[savedItems.Count]; savedItems.CopyTo(newItems, 0); } // Store the current list of items // if (newItems != null) { Items.AddRangeInternal(newItems); } // Restore the selected indices if SelectionMode allows it. // if (SelectionMode != SelectionMode.None) { if (this.DataManager != null) { // put the selectedIndex in [....] w/ the position in the dataManager this.SelectedIndex = this.DataManager.Position; } else { if (savedItems != null) { int cnt = savedItems.Count; for(int index = 0; index < cnt; index++) { if (savedItems.InnerArray.GetState(index, SelectedObjectCollection.SelectedObjectMask)) { SelectedItem = savedItems[index]; } } } } } } /// /// /// Reparses the object at the given index, getting new text string for it. /// ///protected override void RefreshItem(int index) { Items.SetItemInternal(index, Items[index]); } public override void ResetBackColor() { base.ResetBackColor(); } public override void ResetForeColor() { base.ResetForeColor(); } private void ResetItemHeight() { itemHeight = DefaultItemHeight; } [SuppressMessage("Microsoft.Portability", "CA1902:AvoidTestingForFloatingPointEquality")] protected override void ScaleControl(SizeF factor, BoundsSpecified specified) { if (factor.Width != 1F && factor.Height != 1F) { UpdateFontCache(); } base.ScaleControl(factor, specified); } /// /// /// Overrides Control.SetBoundsCore to remember the requestedHeight. /// ///protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { // Avoid the listbox and textbox behaviour in Collection editors // if (!integralHeightAdjust && height != Height) requestedHeight = height; base.SetBoundsCore(x, y, width, height, specified); } /// /// /// Performs the work of setting the specified items into the ListBox. /// protected override void SetItemsCore(IList value) { BeginUpdate(); Items.ClearInternal(); Items.AddRangeInternal(value); this.SelectedItems.Dirty(); // if the list changed, we want to keep the same selected index // CurrencyManager will provide the PositionChanged event // it will be provided before changing the list though... if (this.DataManager != null) { if (this.DataSource is ICurrencyManagerProvider) { // Everett ListControl's had a bug where they would not fire // OnSelectedValueChanged if their list of items were refreshed. // We fix this post-Everett. // However, for APPCOMPAT reasons, we only want to fix it when binding to // Whidbey components. // vsw 547279. this.selectedValueChangedFired = false; } if (IsHandleCreated) { SendMessage(NativeMethods.LB_SETCURSEL, DataManager.Position, 0); } // if the list changed and we still did not fire the // onselectedChanged event, then fire it now; if (!selectedValueChangedFired) { OnSelectedValueChanged(EventArgs.Empty); selectedValueChangedFired = false; } } EndUpdate(); } ///protected override void SetItemCore(int index, object value) { Items.SetItemInternal(index, value); } /// /// /// Allows the user to set an item as being selected or not. This should /// only be used with ListBoxes that allow some sort of multi-selection. /// public void SetSelected(int index, bool value) { int itemCount = (itemsCollection == null) ? 0: itemsCollection.Count; if (index < 0 || index >= itemCount) throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); if (selectionMode == SelectionMode.None) throw new InvalidOperationException(SR.GetString(SR.ListBoxInvalidSelectionMode)); SelectedItems.SetSelected(index, value); if (IsHandleCreated) { NativeSetSelected(index, value); } SelectedItems.Dirty(); OnSelectedIndexChanged(EventArgs.Empty); } ////// /// Sorts the items in the listbox. /// protected virtual void Sort() { // This will force the collection to add each item back to itself // if sorted is now true, then the add method will insert the item // into the correct position // CheckNoDataSource(); SelectedObjectCollection currentSelections = SelectedItems; currentSelections.EnsureUpToDate(); if (sorted && itemsCollection != null) { itemsCollection.InnerArray.Sort(); // Now that we've sorted, update our handle // if it has been created. if (IsHandleCreated) { NativeClear(); int count = itemsCollection.Count; for(int i = 0; i < count; i++) { NativeAdd(itemsCollection[i]); if (currentSelections.GetSelected(i)) { NativeSetSelected(i, true); } } } } } ////// /// Returns a string representation for this control. /// ///public override string ToString() { string s = base.ToString(); if (itemsCollection != null) { s += ", Items.Count: " + Items.Count.ToString(CultureInfo.CurrentCulture); if (Items.Count > 0) { string z = GetItemText(Items[0]); string txt = (z.Length > 40) ? z.Substring(0, 40) : z; s += ", Items[0]: " + txt; } } return s; } private void UpdateFontCache() { fontIsChanged = true; integralHeightAdjust = true; try { Height = requestedHeight; } finally { integralHeightAdjust = false; } maxWidth = -1; UpdateHorizontalExtent(); // clear the preferred size cache. CommonProperties.xClearPreferredSizeCache(this); } private void UpdateHorizontalExtent() { if (!multiColumn && horizontalScrollbar && IsHandleCreated) { int width = horizontalExtent; if (width == 0) { width = MaxItemWidth; } SendMessage(NativeMethods.LB_SETHORIZONTALEXTENT, width, 0); } } // Updates the cached max item width // private void UpdateMaxItemWidth(object item, bool removing) { // We shouldn't be caching maxWidth if we don't have horizontal scrollbars, // or horizontal extent has been set // if (!horizontalScrollbar || horizontalExtent > 0) { maxWidth = -1; return; } // Only update if we are currently caching maxWidth // if (maxWidth > -1) { // Compute item width // int width; using (Graphics graphics = CreateGraphicsInternal()) { width = (int)(Math.Ceiling(graphics.MeasureString(GetItemText(item), this.Font).Width)); } if (removing) { // We're removing this item, so if it's the longest // in the list, reset the cache // if (width >= maxWidth) { maxWidth = -1; } } else { // We're adding or inserting this item - update the cache // if (width > maxWidth) { maxWidth = width; } } } } // Updates the Custom TabOffsets // private void UpdateCustomTabOffsets() { if (IsHandleCreated && UseCustomTabOffsets && CustomTabOffsets != null) { int wpar = CustomTabOffsets.Count; int[] offsets = new int[wpar]; CustomTabOffsets.CopyTo(offsets, 0); UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LB_SETTABSTOPS, wpar, offsets); Invalidate(); } } private void WmPrint(ref Message m) { base.WndProc(ref m); if ((NativeMethods.PRF_NONCLIENT & (int)m.LParam) != 0 && Application.RenderWithVisualStyles && this.BorderStyle == BorderStyle.Fixed3D) { IntSecurity.UnmanagedCode.Assert(); try { using (Graphics g = Graphics.FromHdc(m.WParam)) { Rectangle rect = new Rectangle(0, 0, this.Size.Width - 1, this.Size.Height - 1); g.DrawRectangle(new Pen(VisualStyleInformation.TextControlBorder), rect); rect.Inflate(-1, -1); g.DrawRectangle(SystemPens.Window, rect); } } finally { CodeAccessPermission.RevertAssert(); } } } /// /// /// ///[ System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode), System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode) ] protected virtual void WmReflectCommand(ref Message m) { switch (NativeMethods.Util.HIWORD(m.WParam)) { case NativeMethods.LBN_SELCHANGE: if (selectedItems != null) { selectedItems.Dirty(); } OnSelectedIndexChanged(EventArgs.Empty); break; case NativeMethods.LBN_DBLCLK: // Handle this inside WM_LBUTTONDBLCLK // OnDoubleClick(EventArgs.Empty); break; } } /// /// /// ///private void WmReflectDrawItem(ref Message m) { NativeMethods.DRAWITEMSTRUCT dis = (NativeMethods.DRAWITEMSTRUCT)m.GetLParam(typeof(NativeMethods.DRAWITEMSTRUCT)); IntPtr dc = dis.hDC; IntPtr oldPal = SetUpPalette(dc, false /*force*/, false /*realize*/); try { Graphics g = Graphics.FromHdcInternal(dc); try { Rectangle bounds = Rectangle.FromLTRB(dis.rcItem.left, dis.rcItem.top, dis.rcItem.right, dis.rcItem.bottom); if (HorizontalScrollbar) { if (MultiColumn) { bounds.Width = Math.Max(ColumnWidth, bounds.Width); } else { bounds.Width = Math.Max(MaxItemWidth, bounds.Width); } } OnDrawItem(new DrawItemEventArgs(g, Font, bounds, dis.itemID, (DrawItemState)dis.itemState, ForeColor, BackColor)); } finally { g.Dispose(); } } finally { if (oldPal != IntPtr.Zero) { SafeNativeMethods.SelectPalette(new HandleRef(null, dc), new HandleRef(null, oldPal), 0); } } m.Result = (IntPtr)1; } /// /// /// ///// This method is only called if in owner draw mode private void WmReflectMeasureItem(ref Message m) { NativeMethods.MEASUREITEMSTRUCT mis = (NativeMethods.MEASUREITEMSTRUCT)m.GetLParam(typeof(NativeMethods.MEASUREITEMSTRUCT)); if (drawMode == DrawMode.OwnerDrawVariable && mis.itemID >= 0) { Graphics graphics = CreateGraphicsInternal(); MeasureItemEventArgs mie = new MeasureItemEventArgs(graphics, mis.itemID, ItemHeight); try { OnMeasureItem(mie); mis.itemHeight = mie.ItemHeight; } finally { graphics.Dispose(); } } else { mis.itemHeight = ItemHeight; } Marshal.StructureToPtr(mis, m.LParam, false); m.Result = (IntPtr)1; } /// /// /// The list's window procedure. Inheriting classes can override this /// to add extra functionality, but should not forget to call /// base.wndProc(m); to ensure the list continues to function properly. /// [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] protected override void WndProc(ref Message m) { switch (m.Msg) { case NativeMethods.WM_REFLECT + NativeMethods.WM_COMMAND: WmReflectCommand(ref m); break; case NativeMethods.WM_REFLECT + NativeMethods.WM_DRAWITEM: WmReflectDrawItem(ref m); break; case NativeMethods.WM_REFLECT + NativeMethods.WM_MEASUREITEM: WmReflectMeasureItem(ref m); break; case NativeMethods.WM_PRINT: WmPrint(ref m); break; case NativeMethods.WM_LBUTTONDOWN: if (selectedItems != null) { selectedItems.Dirty(); } base.WndProc(ref m); break; case NativeMethods.WM_LBUTTONUP: // Get the mouse location // int x = NativeMethods.Util.SignedLOWORD(m.LParam); int y = NativeMethods.Util.SignedHIWORD(m.LParam); Point pt = new Point(x,y); pt = PointToScreen(pt); bool captured = Capture; if (captured && UnsafeNativeMethods.WindowFromPoint(pt.X, pt.Y) == Handle) { if (!doubleClickFired && !ValidationCancelled) { OnClick(new MouseEventArgs(MouseButtons.Left, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); OnMouseClick(new MouseEventArgs(MouseButtons.Left, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); } else { doubleClickFired = false; // WM_COMMAND is only fired if the user double clicks an item, // so we can't use that as a double-click substitute if (!ValidationCancelled) { OnDoubleClick(new MouseEventArgs(MouseButtons.Left, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); OnMouseDoubleClick(new MouseEventArgs(MouseButtons.Left, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); } } } // // If this control has been disposed in the user's event handler, then we need to ignore the WM_LBUTTONUP // message to avoid exceptions thrown as a result of handle re-creation (VSWhidbey#95150). // We handle this situation here and not at the top of the window procedure since this is the only place // where we can get disposed as an effect of external code (form.Close() for instance) and then pass the // message to the base class. // if (GetState(STATE_DISPOSED)) { base.DefWndProc(ref m); } else { base.WndProc(ref m); } doubleClickFired = false; break; case NativeMethods.WM_RBUTTONUP: // Get the mouse location // int rx = NativeMethods.Util.SignedLOWORD(m.LParam); int ry = NativeMethods.Util.SignedHIWORD(m.LParam); Point rpt = new Point(rx,ry); rpt = PointToScreen(rpt); bool rCaptured = Capture; if (rCaptured && UnsafeNativeMethods.WindowFromPoint(rpt.X, rpt.Y) == Handle) { if (selectedItems != null) { selectedItems.Dirty(); } } base.WndProc(ref m); break; case NativeMethods.WM_LBUTTONDBLCLK: //the Listbox gets WM_LBUTTONDOWN - WM_LBUTTONUP -WM_LBUTTONDBLCLK - WM_LBUTTONUP... //sequence for doubleclick... //the first WM_LBUTTONUP, resets the flag for Doubleclick //So its necessary for us to set it again... doubleClickFired = true; base.WndProc(ref m); break; case NativeMethods.WM_WINDOWPOSCHANGED: base.WndProc(ref m); if (integralHeight && fontIsChanged) { Height = Math.Max(Height,ItemHeight); fontIsChanged = false; } break; default: base.WndProc(ref m); break; } } ////// This is similar to ArrayList except that it also /// mantains a bit-flag based state element for each item /// in the array. /// /// The methods to enumerate, count and get data support /// virtualized indexes. Indexes are virtualized according /// to the state mask passed in. This allows ItemArray /// to be the backing store for one read-write "master" /// collection and serveral read-only collections based /// on masks. ItemArray supports up to 31 masks. /// internal class ItemArray : IComparer { private static int lastMask = 1; private ListControl listControl; private Entry[] entries; private int count; private int version; public ItemArray(ListControl listControl) { this.listControl = listControl; } ////// The version of this array. This number changes with each /// change to the item list. /// public int Version { get { return version; } } ////// Adds the given item to the array. The state is initially /// zero. /// public object Add(object item) { EnsureSpace(1); version++; entries[count] = new Entry(item); return entries[count++]; } ////// Adds the given collection of items to the array. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public void AddRange(ICollection items) { if (items == null) { throw new ArgumentNullException("items"); } EnsureSpace(items.Count); foreach(object i in items) { entries[count++] = new Entry(i); } version++; } ////// Clears this array. /// public void Clear() { count = 0; version++; } ////// Allocates a new bitmask for use. /// public static int CreateMask() { int mask = lastMask; lastMask = lastMask << 1; Debug.Assert(lastMask > mask, "We have overflowed our state mask."); return mask; } ////// Ensures that our internal array has space for /// the requested # of elements. /// private void EnsureSpace(int elements) { if (entries == null) { entries = new Entry[Math.Max(elements, 4)]; } else if (count + elements >= entries.Length) { int newLength = Math.Max(entries.Length * 2, entries.Length + elements); Entry[] newEntries = new Entry[newLength]; entries.CopyTo(newEntries, 0); entries = newEntries; } } ////// Turns a virtual index into an actual index. /// public int GetActualIndex(int virtualIndex, int stateMask) { if (stateMask == 0) { return virtualIndex; } // More complex; we must compute this index. int calcIndex = -1; for(int i = 0; i < count; i++) { if ((entries[i].state & stateMask) != 0) { calcIndex++; if (calcIndex == virtualIndex) { return i; } } } return -1; } ////// Gets the count of items matching the given mask. /// public int GetCount(int stateMask) { // If mask is zero, then just give the main count if (stateMask == 0) { return count; } // more complex: must provide a count of items // based on a mask. int filteredCount = 0; for(int i = 0; i < count; i++) { if ((entries[i].state & stateMask) != 0) { filteredCount++; } } return filteredCount; } ////// Retrieves an enumerator that will enumerate based on /// the given mask. /// public IEnumerator GetEnumerator(int stateMask) { return GetEnumerator(stateMask, false); } ////// Retrieves an enumerator that will enumerate based on /// the given mask. /// public IEnumerator GetEnumerator(int stateMask, bool anyBit) { return new EntryEnumerator(this, stateMask, anyBit); } ////// Gets the item at the given index. The index is /// virtualized against the given mask value. /// public object GetItem(int virtualIndex, int stateMask) { int actualIndex = GetActualIndex(virtualIndex, stateMask); if (actualIndex == -1) { throw new IndexOutOfRangeException(); } return entries[actualIndex].item; } ////// Gets the item at the given index. The index is /// virtualized against the given mask value. /// internal object GetEntryObject(int virtualIndex, int stateMask) { int actualIndex = GetActualIndex(virtualIndex, stateMask); if (actualIndex == -1) { throw new IndexOutOfRangeException(); } return entries[actualIndex]; } ////// Returns true if the requested state mask is set. /// The index is the actual index to the array. /// public bool GetState(int index, int stateMask) { return ((entries[index].state & stateMask) == stateMask); } ////// Returns the virtual index of the item based on the /// state mask. /// public int IndexOf(object item, int stateMask) { int virtualIndex = -1; for(int i = 0; i < count; i++) { if (stateMask == 0 || (entries[i].state & stateMask) != 0) { virtualIndex++; if (entries[i].item.Equals(item)) { return virtualIndex; } } } return -1; } ////// Returns the virtual index of the item based on the /// state mask. Uses reference equality to identify the /// given object in the list. /// public int IndexOfIdentifier(object identifier, int stateMask) { int virtualIndex = -1; for(int i = 0; i < count; i++) { if (stateMask == 0 || (entries[i].state & stateMask) != 0) { virtualIndex++; if (entries[i] == identifier) { return virtualIndex; } } } return -1; } ////// Inserts item at the given index. The index /// is not virtualized. /// public void Insert(int index, object item) { EnsureSpace(1); if (index < count) { System.Array.Copy(entries, index, entries, index + 1, count - index); } entries[index] = new Entry(item); count++; version++; } ////// Removes the given item from the array. If /// the item is not in the array, this does nothing. /// public void Remove(object item) { int index = IndexOf(item, 0); if (index != -1) { RemoveAt(index); } } ////// Removes the item at the given index. /// public void RemoveAt(int index) { count--; for (int i = index; i < count; i++) { entries[i] = entries[i+1]; } entries[count] = null; version++; } ////// Sets the item at the given index to a new value. /// public void SetItem(int index, object item) { entries[index].item = item; } ////// Sets the state data for the given index. /// public void SetState(int index, int stateMask, bool value) { if (value) { entries[index].state |= stateMask; } else { entries[index].state &= ~stateMask; } version++; } ////// Find element in sorted array. If element is not found returns a binary complement of index for inserting /// public int BinarySearch(object element) { return Array.BinarySearch(entries, 0, count, element, this); } ////// Sorts our array. /// public void Sort() { Array.Sort(entries, 0, count, this); } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public void Sort(Array externalArray) { Array.Sort(externalArray, this); } int IComparer.Compare(object item1, object item2) { if (item1 == null) { if (item2 == null) return 0; //both null, then they are equal return -1; //item1 is null, but item2 is valid (greater) } if (item2 == null) return 1; //item2 is null, so item 1 is greater if (item1 is Entry) { item1 = ((Entry)item1).item; } if (item2 is Entry) { item2 = ((Entry)item2).item; } String itemName1 = listControl.GetItemText(item1); String itemName2 = listControl.GetItemText(item2); CompareInfo compInfo = (Application.CurrentCulture).CompareInfo; return compInfo.Compare(itemName1, itemName2, CompareOptions.StringSort); } ////// This is a single entry in our item array. /// private class Entry { public object item; public int state; public Entry(object item) { this.item = item; this.state = 0; } } ////// EntryEnumerator is an enumerator that will enumerate over /// a given state mask. /// private class EntryEnumerator : IEnumerator { private ItemArray items; private bool anyBit; private int state; private int current; private int version; ////// Creates a new enumerator that will enumerate over the given state. /// public EntryEnumerator(ItemArray items, int state, bool anyBit) { this.items = items; this.state = state; this.anyBit = anyBit; this.version = items.version; this.current = -1; } ////// Moves to the next element, or returns false if at the end. /// bool IEnumerator.MoveNext() { if(version != items.version) throw new InvalidOperationException(SR.GetString(SR.ListEnumVersionMismatch)); while(true) { if (current < items.count - 1) { current++; if (anyBit) { if ((items.entries[current].state & state) != 0) { return true; } } else { if ((items.entries[current].state & state) == state) { return true; } } } else { current = items.count; return false; } } } ////// Resets the enumeration back to the beginning. /// void IEnumerator.Reset() { if(version != items.version) throw new InvalidOperationException(SR.GetString(SR.ListEnumVersionMismatch)); current = -1; } ////// Retrieves the current value in the enumerator. /// object IEnumerator.Current { get { if (current == -1 || current == items.count) { throw new InvalidOperationException(SR.GetString(SR.ListEnumCurrentOutOfRange)); } return items.entries[current].item; } } } } // Items ////// /// [ListBindable(false)] public class ObjectCollection : IList { private ListBox owner; private ItemArray items; ////// A collection that stores objects. /// ////// /// public ObjectCollection(ListBox owner) { this.owner = owner; } ///[To be supplied.] ////// /// public ObjectCollection(ListBox owner, ObjectCollection value) { this.owner = owner; this.AddRange(value); } ////// Initializes a new instance of ListBox.ObjectCollection based on another ListBox.ObjectCollection. /// ////// /// public ObjectCollection(ListBox owner, object[] value) { this.owner = owner; this.AddRange(value); } ////// Initializes a new instance of ListBox.ObjectCollection containing any array of objects. /// ////// /// Retrieves the number of items. /// public int Count { get { return InnerArray.GetCount(0); } } ////// Internal access to the actual data store. /// internal ItemArray InnerArray { get { if (items == null) { items = new ItemArray(owner); } return items; } } ////// object ICollection.SyncRoot { get { return this; } } /// /// bool ICollection.IsSynchronized { get { return false; } } /// /// bool IList.IsFixedSize { get { return false; } } /// /// /// public bool IsReadOnly { get { return false; } } ///[To be supplied.] ////// /// Adds an item to the List box. For an unsorted List box, the item is /// added to the end of the existing list of items. For a sorted List box, /// the item is inserted into the list according to its sorted position. /// The item's toString() method is called to obtain the string that is /// displayed in the combo box. /// A SystemException occurs if there is insufficient space available to /// store the new item. /// public int Add(object item) { owner.CheckNoDataSource(); int index = AddInternal(item); owner.UpdateHorizontalExtent(); return index; } private int AddInternal(object item) { if (item == null) { throw new ArgumentNullException("item"); } int index = -1; if (!owner.sorted) { InnerArray.Add(item); } else { if (Count > 0) { index = InnerArray.BinarySearch(item); if (index < 0) { index = ~index; // getting the index of the first element that is larger than the search value //this index will be used for insert } } else index = 0; Debug.Assert(index >= 0 && index <= Count, "Wrong index for insert"); InnerArray.Insert(index, item); } bool successful = false; try { if (owner.sorted) { if (owner.IsHandleCreated) { owner.NativeInsert(index, item); owner.UpdateMaxItemWidth(item, false); if (owner.selectedItems != null) { // VSWhidbey 95187: sorting may throw the LB contents and the selectedItem array out of synch. owner.selectedItems.Dirty(); } } } else { index = Count - 1; if (owner.IsHandleCreated) { owner.NativeAdd(item); owner.UpdateMaxItemWidth(item, false); } } successful = true; } finally { if (!successful) { InnerArray.Remove(item); } } return index; } ////// int IList.Add(object item) { return Add(item); } /// /// /// public void AddRange(ObjectCollection value) { owner.CheckNoDataSource(); AddRangeInternal((ICollection)value); } ///[To be supplied.] ////// /// public void AddRange(object[] items) { owner.CheckNoDataSource(); AddRangeInternal((ICollection)items); } internal void AddRangeInternal(ICollection items) { if (items == null) { throw new ArgumentNullException("items"); } owner.BeginUpdate(); try { foreach (object item in items) { // adding items one-by-one for performance // not using sort because after the array is sorted index of each newly added item will need to be found // AddInternal is based on BinarySearch and finds index without any additional cost AddInternal(item); } } finally { owner.UpdateHorizontalExtent(); owner.EndUpdate(); } } ///[To be supplied.] ////// /// Retrieves the item with the specified index. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public virtual object this[int index] { get { if (index < 0 || index >= InnerArray.GetCount(0)) { throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); } return InnerArray.GetItem(index, 0); } set { owner.CheckNoDataSource(); SetItemInternal(index, value); } } ////// /// Removes all items from the ListBox. /// public virtual void Clear() { owner.CheckNoDataSource(); ClearInternal(); } ////// Removes all items from the ListBox. Bypasses the data source check. /// internal void ClearInternal() { //update the width.. to reset Scrollbars.. // Clear the selection state. // int cnt = owner.Items.Count; for (int i = 0; i < cnt; i++) { owner.UpdateMaxItemWidth(InnerArray.GetItem(i, 0), true); } if (owner.IsHandleCreated) { owner.NativeClear(); } InnerArray.Clear(); owner.maxWidth = -1; owner.UpdateHorizontalExtent(); } ////// /// public bool Contains(object value) { return IndexOf(value) != -1; } ///[To be supplied.] ////// /// Copies the ListBox Items collection to a destination array. /// public void CopyTo(object[] destination, int arrayIndex) { int count = InnerArray.GetCount(0); for(int i = 0; i < count; i++) { destination[i + arrayIndex] = InnerArray.GetItem(i, 0); } } ////// void ICollection.CopyTo(Array destination, int index) { int count = InnerArray.GetCount(0); for(int i = 0; i < count; i++) { destination.SetValue(InnerArray.GetItem(i, 0), i + index); } } /// /// /// Returns an enumerator for the ListBox Items collection. /// public IEnumerator GetEnumerator() { return InnerArray.GetEnumerator(0); } ////// /// public int IndexOf(object value) { if (value == null) { throw new ArgumentNullException("value"); } return InnerArray.IndexOf(value,0); } ///[To be supplied.] ////// /// ///[To be supplied.] ///internal int IndexOfIdentifier(object value) { if (value == null) { throw new ArgumentNullException("value"); } return InnerArray.IndexOfIdentifier(value,0); } /// /// /// Adds an item to the combo box. For an unsorted combo box, the item is /// added to the end of the existing list of items. For a sorted combo box, /// the item is inserted into the list according to its sorted position. /// The item's toString() method is called to obtain the string that is /// displayed in the combo box. /// A SystemException occurs if there is insufficient space available to /// store the new item. /// public void Insert(int index, object item) { owner.CheckNoDataSource(); if (index < 0 || index > InnerArray.GetCount(0)) { throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); } // If the combo box is sorted, then nust treat this like an add // because we are going to twiddle the index anyway. // if (owner.sorted) { Add(item); } else { InnerArray.Insert(index, item); if (owner.IsHandleCreated) { bool successful = false; try { owner.NativeInsert(index, item); owner.UpdateMaxItemWidth(item, false); successful = true; } finally { if (!successful) { InnerArray.RemoveAt(index); } } } } owner.UpdateHorizontalExtent(); } ////// /// Removes the given item from the ListBox, provided that it is /// actually in the list. /// public void Remove(object value) { int index = InnerArray.IndexOf(value, 0); if (index != -1) { RemoveAt(index); } } ////// /// Removes an item from the ListBox at the given index. /// public void RemoveAt(int index) { owner.CheckNoDataSource(); if (index < 0 || index >= InnerArray.GetCount(0)) { throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); } owner.UpdateMaxItemWidth(InnerArray.GetItem(index, 0), true); // VSWhidbey 95181: Update InnerArray before calling NativeRemoveAt to ensure that when // SelectedIndexChanged is raised (by NativeRemoveAt), InnerArray's state matches wrapped LB state. InnerArray.RemoveAt(index); if (owner.IsHandleCreated) { owner.NativeRemoveAt(index); } owner.UpdateHorizontalExtent(); } internal void SetItemInternal(int index, object value) { if (value == null) { throw new ArgumentNullException("value"); } if (index < 0 || index >= InnerArray.GetCount(0)) { throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); } owner.UpdateMaxItemWidth(InnerArray.GetItem(index, 0), true); InnerArray.SetItem(index, value); // If the native control has been created, and the display text of the new list item object // is different to the current text in the native list item, recreate the native list item... if (owner.IsHandleCreated) { bool selected = (owner.SelectedIndex == index); if (String.Compare(this.owner.GetItemText(value), this.owner.NativeGetItemText(index), true, CultureInfo.CurrentCulture) != 0) { owner.NativeRemoveAt(index); owner.SelectedItems.SetSelected(index, false); owner.NativeInsert(index, value); owner.UpdateMaxItemWidth(value, false); if (selected) { owner.SelectedIndex = index; } } else { // NEW - FOR COMPATIBILITY REASONS // Minimum compatibility fix for VSWhidbey 377287 if (selected) { owner.OnSelectedIndexChanged(EventArgs.Empty); //will fire selectedvaluechanged } } } owner.UpdateHorizontalExtent(); } } // end ObjectCollection //***************************************************************************************** // IntegerCollection ////// /// public class IntegerCollection : IList { private ListBox owner; private int[] innerArray; private int count=0; ///[To be supplied.] ////// /// public IntegerCollection(ListBox owner) { this.owner = owner; } ///[To be supplied.] ////// /// [Browsable(false)] public int Count { get { return count; } } ///Number of current selected items. ////// object ICollection.SyncRoot { get { return this; } } /// /// bool ICollection.IsSynchronized { get { return true; } } /// /// bool IList.IsFixedSize { get { return false; } } /// /// /// bool IList.IsReadOnly { get { return false; } } ///[To be supplied.] ////// /// public bool Contains(int item) { return IndexOf(item) != -1; } ///[To be supplied.] ////// bool IList.Contains(object item) { if (item is Int32) { return Contains((int)item); } else { return false; } } public void Clear() { count = 0; innerArray = null; } /// /// /// public int IndexOf(int item) { return Array.IndexOf(innerArray, item); } ///[To be supplied.] ////// int IList.IndexOf(object item) { if (item is Int32) { return IndexOf((int)item); } else { return -1; } } /// /// Add a unique integer to the collection in sorted order. /// A SystemException occurs if there is insufficient space available to /// store the new item. /// private int AddInternal(int item) { EnsureSpace(1); int index = IndexOf(item); if (index == -1) { innerArray[count++] = item; Array.Sort(innerArray,0,count); index = IndexOf(item); } return index; } ////// /// Adds a unique integer to the collection in sorted order. /// A SystemException occurs if there is insufficient space available to /// store the new item. /// public int Add(int item) { int index = AddInternal(item); owner.UpdateCustomTabOffsets(); return index; } ////// [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] [ SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "item" is the name of the param passed in. // So we don't have to localize it. ] int IList.Add(object item) { if (!(item is int)) { throw new ArgumentException("item"); } return Add((int)item); } /// /// /// public void AddRange(int[] items) { AddRangeInternal((ICollection)items); } ///[To be supplied.] ////// /// public void AddRange(IntegerCollection value) { AddRangeInternal((ICollection)value); } ///[To be supplied.] ////// Add range that bypasses the data source check. /// [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] [ SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "item" is the name of the param passed in. // So we don't have to localize it. ] private void AddRangeInternal(ICollection items) { if (items == null) { throw new ArgumentNullException("items"); } owner.BeginUpdate(); try { EnsureSpace(items.Count); foreach(object item in items) { if (!(item is int)) { throw new ArgumentException("item"); } else { AddInternal((int)item); } } owner.UpdateCustomTabOffsets(); } finally { owner.EndUpdate(); } } ////// Ensures that our internal array has space for /// the requested # of elements. /// private void EnsureSpace(int elements) { if (innerArray == null) { innerArray = new int[Math.Max(elements, 4)]; } else if (count + elements >= innerArray.Length) { int newLength = Math.Max(innerArray.Length * 2, innerArray.Length + elements); int[] newEntries = new int[newLength]; innerArray.CopyTo(newEntries, 0); innerArray = newEntries; } } ////// void IList.Clear() { Clear(); } /// /// void IList.Insert(int index, object value) { throw new NotSupportedException(SR.GetString(SR.ListBoxCantInsertIntoIntegerCollection)); } /// /// [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] [ SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "value" is the name of the param passed in. // So we don't have to localize it. ] void IList.Remove(object value) { if (!(value is int)) { throw new ArgumentException("value"); } Remove((int)value); } /// /// void IList.RemoveAt(int index) { RemoveAt(index); } /// /// /// Removes the given item from the array. If /// the item is not in the array, this does nothing. /// public void Remove(int item) { int index = IndexOf(item); if (index != -1) { RemoveAt(index); } } ////// /// Removes the item at the given index. /// public void RemoveAt(int index) { count--; for (int i = index; i < count; i++) { innerArray[i] = innerArray[i+1]; } } ////// /// Retrieves the specified selected item. /// [ SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "index" is the name of the param passed in. // So we don't have to localize it. ] public int this[int index] { get { return innerArray[index]; } [ SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly") // This exception already shipped. // We can't change its text. ] set { if (index < 0 || index >= count) { throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); } innerArray[index] = (int)value; owner.UpdateCustomTabOffsets(); } } ////// [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] object IList.this[int index] { get { return this[index]; } [ SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters"), // "value" is the name of the param. // So we don't have to localize it. SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly") // This exception already shipped. // We can't change its text. ] set { if (!(value is int)) { throw new ArgumentException("value"); } else { this[index] = (int)value; } } } /// /// /// public void CopyTo(Array destination, int index) { int cnt = Count; for (int i = 0; i < cnt; i++) { destination.SetValue(this[i], i + index); } } ///[To be supplied.] ////// /// IEnumerator IEnumerable.GetEnumerator() { return new CustomTabOffsetsEnumerator(this); } ///[To be supplied.] ////// EntryEnumerator is an enumerator that will enumerate over /// a given state mask. /// private class CustomTabOffsetsEnumerator : IEnumerator { private IntegerCollection items; private int current; ////// Creates a new enumerator that will enumerate over the given state. /// public CustomTabOffsetsEnumerator(IntegerCollection items) { this.items = items; this.current = -1; } ////// Moves to the next element, or returns false if at the end. /// bool IEnumerator.MoveNext() { if (current < items.Count - 1) { current++; return true; } else { current = items.Count; return false; } } ////// Resets the enumeration back to the beginning. /// void IEnumerator.Reset() { current = -1; } ////// Retrieves the current value in the enumerator. /// object IEnumerator.Current { get { if (current == -1 || current == items.Count) { throw new InvalidOperationException(SR.GetString(SR.ListEnumCurrentOutOfRange)); } return items[current]; } } } } //***************************************************************************************** // SelectedIndices ////// /// public class SelectedIndexCollection : IList { private ListBox owner; /* C#r: protected */ ///[To be supplied.] ////// /// public SelectedIndexCollection(ListBox owner) { this.owner = owner; } ///[To be supplied.] ////// /// [Browsable(false)] public int Count { get { return owner.SelectedItems.Count; } } ///Number of current selected items. ////// object ICollection.SyncRoot { get { return this; } } /// /// bool ICollection.IsSynchronized { get { return true; } } /// /// bool IList.IsFixedSize { get { return true; } } /// /// /// public bool IsReadOnly { get { return true; } } ///[To be supplied.] ////// /// public bool Contains(int selectedIndex) { return IndexOf(selectedIndex) != -1; } ///[To be supplied.] ////// bool IList.Contains(object selectedIndex) { if (selectedIndex is Int32) { return Contains((int)selectedIndex); } else { return false; } } /// /// /// public int IndexOf(int selectedIndex) { // Just what does this do? The selectedIndex parameter above is the index into the // main object collection. We look at the state of that item, and if the state indicates // that it is selected, we get back the virtualized index into this collection. Indexes on // this collection match those on the SelectedObjectCollection. if (selectedIndex >= 0 && selectedIndex < InnerArray.GetCount(0) && InnerArray.GetState(selectedIndex, SelectedObjectCollection.SelectedObjectMask)) { return InnerArray.IndexOf(InnerArray.GetItem(selectedIndex, 0), SelectedObjectCollection.SelectedObjectMask); } return -1; } ///[To be supplied.] ////// int IList.IndexOf(object selectedIndex) { if (selectedIndex is Int32) { return IndexOf((int)selectedIndex); } else { return -1; } } /// /// int IList.Add(object value) { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); } /// /// void IList.Clear() { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); } /// /// void IList.Insert(int index, object value) { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); } /// /// void IList.Remove(object value) { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); } /// /// void IList.RemoveAt(int index) { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); } /// /// /// Retrieves the specified selected item. /// public int this[int index] { get { object identifier = InnerArray.GetEntryObject(index, SelectedObjectCollection.SelectedObjectMask); return InnerArray.IndexOfIdentifier(identifier, 0); } } ////// object IList.this[int index] { get { return this[index]; } set { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); } } /// /// This is the item array that stores our data. We share this backing store /// with the main object collection. /// private ItemArray InnerArray { get { owner.SelectedItems.EnsureUpToDate(); return ((ObjectCollection)owner.Items).InnerArray; } } ////// /// public void CopyTo(Array destination, int index) { int cnt = Count; for (int i = 0; i < cnt; i++) { destination.SetValue(this[i], i + index); } } ///[To be supplied.] ////// /// public void Clear() { if (owner != null) { owner.ClearSelected(); } } ///[To be supplied.] ////// /// public void Add(int index) { if (owner != null) { ObjectCollection items = owner.Items; if (items != null) { if (index != -1 && !Contains(index)) { owner.SetSelected(index, true); } } } } ///[To be supplied.] ////// /// public void Remove(int index) { if (owner != null) { ObjectCollection items = owner.Items; if (items != null) { if (index != -1 && Contains(index)) { owner.SetSelected(index, false); } } } } ///[To be supplied.] ////// /// public IEnumerator GetEnumerator() { return new SelectedIndexEnumerator(this); } ///[To be supplied.] ////// EntryEnumerator is an enumerator that will enumerate over /// a given state mask. /// private class SelectedIndexEnumerator : IEnumerator { private SelectedIndexCollection items; private int current; ////// Creates a new enumerator that will enumerate over the given state. /// public SelectedIndexEnumerator(SelectedIndexCollection items) { this.items = items; this.current = -1; } ////// Moves to the next element, or returns false if at the end. /// bool IEnumerator.MoveNext() { if (current < items.Count - 1) { current++; return true; } else { current = items.Count; return false; } } ////// Resets the enumeration back to the beginning. /// void IEnumerator.Reset() { current = -1; } ////// Retrieves the current value in the enumerator. /// object IEnumerator.Current { get { if (current == -1 || current == items.Count) { throw new InvalidOperationException(SR.GetString(SR.ListEnumCurrentOutOfRange)); } return items[current]; } } } } // Should be "ObjectCollection", except we already have one of those. ////// /// public class SelectedObjectCollection : IList { // This is the bitmask used within ItemArray to identify selected objects. internal static int SelectedObjectMask = ItemArray.CreateMask(); private ListBox owner; private bool stateDirty; private int lastVersion; private int count; /* C#r: protected */ ///[To be supplied.] ////// /// public SelectedObjectCollection(ListBox owner) { this.owner = owner; this.stateDirty = true; this.lastVersion = -1; } ///[To be supplied.] ////// /// Number of current selected items. /// public int Count { get { if (owner.IsHandleCreated) { SelectionMode current = (owner.selectionModeChanging) ? owner.cachedSelectionMode : owner.selectionMode; switch (current) { case SelectionMode.None: return 0; case SelectionMode.One: int index = owner.SelectedIndex; if (index >= 0) { return 1; } return 0; case SelectionMode.MultiSimple: case SelectionMode.MultiExtended: return (int)owner.SendMessage(NativeMethods.LB_GETSELCOUNT, 0, 0); } return 0; } // If the handle hasn't been created, we must do this the hard way. // Getting the count when using a mask is expensive, so cache it. // if (lastVersion != InnerArray.Version) { lastVersion = InnerArray.Version; count = InnerArray.GetCount(SelectedObjectMask); } return count; } } ////// object ICollection.SyncRoot { get { return this; } } /// /// bool ICollection.IsSynchronized { get { return false; } } /// /// bool IList.IsFixedSize { get { return true; } } /// /// Called by the list box to dirty the selected item state. /// internal void Dirty() { stateDirty = true; } ////// This is the item array that stores our data. We share this backing store /// with the main object collection. /// private ItemArray InnerArray { get { EnsureUpToDate(); return ((ObjectCollection)owner.Items).InnerArray; } } ////// This is the function that Ensures that the selections are uptodate with /// current listbox handle selections. /// internal void EnsureUpToDate() { if (stateDirty) { stateDirty = false; if (owner.IsHandleCreated) { owner.NativeUpdateSelection(); } } } ////// /// public bool IsReadOnly { get { return true; } } ///[To be supplied.] ////// /// public bool Contains(object selectedObject) { return IndexOf(selectedObject) != -1; } ///[To be supplied.] ////// /// public int IndexOf(object selectedObject) { return InnerArray.IndexOf(selectedObject, SelectedObjectMask); } ///[To be supplied.] ////// int IList.Add(object value) { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); } /// /// void IList.Clear() { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); } /// /// void IList.Insert(int index, object value) { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); } /// /// void IList.Remove(object value) { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); } /// /// void IList.RemoveAt(int index) { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); } // A new internal method used in SelectedIndex getter... // For a Multi select ListBox there can be two items with the same name ... // and hence a object comparison is required... // This method returns the "object" at the passed index rather than the "item" ... // this "object" is then compared in the IndexOf( ) method of the itemsCollection. // /// /// internal object GetObjectAt(int index) { return InnerArray.GetEntryObject(index, SelectedObjectCollection.SelectedObjectMask); } /// /// /// Retrieves the specified selected item. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public object this[int index] { get { return InnerArray.GetItem(index, SelectedObjectMask); } set { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); } } ////// /// public void CopyTo(Array destination, int index) { int cnt = InnerArray.GetCount(SelectedObjectMask); for (int i = 0; i < cnt; i++) { destination.SetValue(InnerArray.GetItem(i, SelectedObjectMask), i + index); } } ///[To be supplied.] ////// /// public IEnumerator GetEnumerator() { return InnerArray.GetEnumerator(SelectedObjectMask); } ///[To be supplied.] ////// This method returns if the actual item index is selected. The index is the index to the MAIN /// collection, not this one. /// internal bool GetSelected(int index) { return InnerArray.GetState(index, SelectedObjectMask); } // when SelectedObjectsCollection::ItemArray is accessed we push the selection from Native ListBox into our .Net ListBox - see EnsureUpToDate() // when we create the handle we need to be able to do the opposite : push the selection from .Net ListBox into Native ListBox internal void PushSelectionIntoNativeListBox(int index) { // we can't use ItemArray accessor because this will wipe out our Selection collection bool selected = ((ObjectCollection)owner.Items).InnerArray.GetState(index, SelectedObjectMask); // push selection only if the item is actually selected // this also takes care of the case where owner.SelectionMode == SelectionMode.One if (selected) { this.owner.NativeSetSelected(index, true /*we signal selection to the native listBox only if the item is actually selected*/); } } ////// Same thing for GetSelected. /// internal void SetSelected(int index, bool value) { InnerArray.SetState(index, SelectedObjectMask, value); } ////// /// public void Clear() { if (owner != null) { owner.ClearSelected(); } } ///[To be supplied.] ////// /// public void Add(object value) { if (owner != null) { ObjectCollection items = owner.Items; if (items != null && value != null) { int index = items.IndexOf(value); if (index != -1 && !GetSelected(index)) { owner.SelectedIndex = index; } } } } ///[To be supplied.] ////// /// public void Remove(object value) { if (owner != null) { ObjectCollection items = owner.Items; if (items != null & value != null) { int index = items.IndexOf(value); if (index != -1 && GetSelected(index)) { owner.SetSelected(index, false); } } } } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. //------------------------------------------------------------------------------ //[To be supplied.] ///// Copyright (c) Microsoft Corporation. All rights reserved. // //----------------------------------------------------------------------------- /* */ namespace System.Windows.Forms { using System.Runtime.Serialization.Formatters; using System.Runtime.Remoting; using System.Runtime.InteropServices; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System; using System.Security; using System.Security.Permissions; using System.Globalization; using System.Windows.Forms.Layout; using System.Drawing.Design; using System.ComponentModel; using System.Windows.Forms.ComponentModel; using System.Windows.Forms.VisualStyles; using System.Collections; using System.Drawing; using Microsoft.Win32; using System.Text; ////// /// /// This is a control that presents a list of items to the user. They may be /// navigated using the keyboard, or the scrollbar on the right side of the /// control. One or more items may be selected as well. /// [ ComVisible(true), ClassInterface(ClassInterfaceType.AutoDispatch), Designer("System.Windows.Forms.Design.ListBoxDesigner, " + AssemblyRef.SystemDesign), DefaultEvent("SelectedIndexChanged"), DefaultProperty("Items"), DefaultBindingProperty("SelectedValue"), SRDescription(SR.DescriptionListBox) ] public class ListBox : ListControl { ////// /// The preferred way to add items is to set them all via an array at once, /// which is definitely the most efficient way. The following is an example /// of this: /// ////// ListBox lb = new ListBox(); /// // set up properties on the listbox here. /// lb.Items.All = new String [] { /// "A", /// "B", /// "C", /// "D"}; ///
////// /// while doing a search, if no matches are found, this is returned /// public const int NoMatches = NativeMethods.LB_ERR; ////// /// The default item height for an owner-draw ListBox. /// public const int DefaultItemHeight = 13; // 13 == listbox's non-ownerdraw item height. That's with Win2k and // the default font; on other platforms and with other fonts, it may be different. private const int maxWin9xHeight = 32767; //Win9x doesn't deal with height > 32K private static readonly object EVENT_SELECTEDINDEXCHANGED = new object(); private static readonly object EVENT_DRAWITEM = new object(); private static readonly object EVENT_MEASUREITEM = new object(); static bool checkedOS = false; static bool runningOnWin2K = true; SelectedObjectCollection selectedItems; SelectedIndexCollection selectedIndices; ObjectCollection itemsCollection; // int itemHeight = DefaultItemHeight; int columnWidth; int requestedHeight; int topIndex; int horizontalExtent = 0; int maxWidth = -1; int updateCount = 0; bool sorted = false; bool scrollAlwaysVisible = false; bool integralHeight = true; bool integralHeightAdjust = false; bool multiColumn = false; bool horizontalScrollbar = false; bool useTabStops = true; bool useCustomTabOffsets = false; bool fontIsChanged = false; bool doubleClickFired = false; bool selectedValueChangedFired = false; DrawMode drawMode = System.Windows.Forms.DrawMode.Normal; BorderStyle borderStyle = System.Windows.Forms.BorderStyle.Fixed3D; SelectionMode selectionMode = System.Windows.Forms.SelectionMode.One; // VsWhidbey : 447524 SelectionMode cachedSelectionMode = System.Windows.Forms.SelectionMode.One; //We need to know that we are in middle of handleRecreate through Setter of SelectionMode. //In this case we set a bool denoting that we are changing SelectionMode and //in this case we should always use the cachedValue instead of the currently set value. //We need to change this in the count as well as SelectedIndex code where we access the SelectionMode. private bool selectionModeChanging = false; ////// This value stores the array of custom tabstops in the listbox. the array should be populated by /// integers in a ascending order. /// private IntegerCollection customTabOffsets; ////// /// Creates a basic win32 list box with default values for everything. /// public ListBox() : base() { SetStyle(ControlStyles.UserPaint | ControlStyles.StandardClick | ControlStyles.UseTextForAccessibility, false); // this class overrides GetPreferredSizeCore, let Control automatically cache the result SetState2(STATE2_USEPREFERREDSIZECACHE, true); SetBounds(0, 0, 120, 96); requestedHeight = Height; } ///public override Color BackColor { get { if (ShouldSerializeBackColor()) { return base.BackColor; } else { return SystemColors.Window; } } set { base.BackColor = value; } } /// /// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public override Image BackgroundImage { get { return base.BackgroundImage; } set { base.BackgroundImage = value; } } ///[To be supplied.] ////// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] new public event EventHandler BackgroundImageChanged { add { base.BackgroundImageChanged += value; } remove { base.BackgroundImageChanged -= value; } } /// /// /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public override ImageLayout BackgroundImageLayout { get { return base.BackgroundImageLayout; } set { base.BackgroundImageLayout = value; } } ///[To be supplied.] ////// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] new public event EventHandler BackgroundImageLayoutChanged { add { base.BackgroundImageLayoutChanged += value; } remove { base.BackgroundImageLayoutChanged -= value; } } /// /// /// Retrieves the current border style. Values for this are taken from /// The System.Windows.Forms.BorderStyle enumeration. /// [ SRCategory(SR.CatAppearance), DefaultValue(BorderStyle.Fixed3D), DispId(NativeMethods.ActiveX.DISPID_BORDERSTYLE), SRDescription(SR.ListBoxBorderDescr) ] public BorderStyle BorderStyle { get { return borderStyle; } set { //valid values are 0x0 to 0x2 if (!ClientUtils.IsEnumValid(value, (int)value, (int)BorderStyle.None, (int)BorderStyle.Fixed3D)){ throw new InvalidEnumArgumentException("value", (int)value, typeof(BorderStyle)); } if (value != borderStyle) { borderStyle = value; RecreateHandle(); // Avoid the listbox and textbox behavior in Collection editors // integralHeightAdjust = true; try { Height = requestedHeight; } finally { integralHeightAdjust = false; } } } } ////// /// [ SRCategory(SR.CatBehavior), Localizable(true), DefaultValue(0), SRDescription(SR.ListBoxColumnWidthDescr) ] public int ColumnWidth { get { return columnWidth; } set { if (value < 0) { throw new ArgumentException(SR.GetString(SR.InvalidLowBoundArgumentEx, "value", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture))); } if (columnWidth != value) { columnWidth = value; // if it's zero, we need to reset, and only way to do // that is to recreate the handle. if (columnWidth == 0) { RecreateHandle(); } else if (IsHandleCreated) { SendMessage(NativeMethods.LB_SETCOLUMNWIDTH, columnWidth, 0); } } } } ////// /// Retrieves the parameters needed to create the handle. Inheriting classes /// can override this to provide extra functionality. They should not, /// however, forget to call base.getCreateParams() first to get the struct /// filled up with the basic info. /// ///protected override CreateParams CreateParams { [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] get { CreateParams cp = base.CreateParams; cp.ClassName = "LISTBOX"; cp.Style |= NativeMethods.WS_VSCROLL | NativeMethods.LBS_NOTIFY | NativeMethods.LBS_HASSTRINGS; if (scrollAlwaysVisible) cp.Style |= NativeMethods.LBS_DISABLENOSCROLL; if (!integralHeight) cp.Style |= NativeMethods.LBS_NOINTEGRALHEIGHT; if (useTabStops) cp.Style |= NativeMethods.LBS_USETABSTOPS; switch (borderStyle) { case BorderStyle.Fixed3D: cp.ExStyle |= NativeMethods.WS_EX_CLIENTEDGE; break; case BorderStyle.FixedSingle: cp.Style |= NativeMethods.WS_BORDER; break; } if (multiColumn) { cp.Style |= NativeMethods.LBS_MULTICOLUMN | NativeMethods.WS_HSCROLL; } else if (horizontalScrollbar) { cp.Style |= NativeMethods.WS_HSCROLL; } switch (selectionMode) { case SelectionMode.None: cp.Style |= NativeMethods.LBS_NOSEL; break; case SelectionMode.MultiSimple: cp.Style |= NativeMethods.LBS_MULTIPLESEL; break; case SelectionMode.MultiExtended: cp.Style |= NativeMethods.LBS_EXTENDEDSEL; break; case SelectionMode.One: break; } switch (drawMode) { case DrawMode.Normal: break; case DrawMode.OwnerDrawFixed: cp.Style |= NativeMethods.LBS_OWNERDRAWFIXED; break; case DrawMode.OwnerDrawVariable: cp.Style |= NativeMethods.LBS_OWNERDRAWVARIABLE; break; } return cp; } } /// /// /// Enables a list box to recognize and expand tab characters when drawing /// its strings using the CustomTabOffsets integer array. /// [ SRCategory(SR.CatBehavior), DefaultValue(false), Browsable(false) ] public bool UseCustomTabOffsets { get { return useCustomTabOffsets; } set { if (useCustomTabOffsets != value) { useCustomTabOffsets = value; RecreateHandle(); } } } ///protected override Size DefaultSize { get { return new Size(120, 96); } } /// /// /// Retrieves the style of the listbox. This will indicate if the system /// draws it, or if the user paints each item manually. It also indicates /// whether or not items have to be of the same height. /// [ SRCategory(SR.CatBehavior), DefaultValue(DrawMode.Normal), SRDescription(SR.ListBoxDrawModeDescr), RefreshProperties(RefreshProperties.Repaint) ] public virtual DrawMode DrawMode { get { return drawMode; } set { //valid values are 0x0 to 0x2 if (!ClientUtils.IsEnumValid(value, (int)value, (int)DrawMode.Normal, (int)DrawMode.OwnerDrawVariable)) { throw new InvalidEnumArgumentException("value", (int)value, typeof(DrawMode)); } if (drawMode != value) { if (MultiColumn && value == DrawMode.OwnerDrawVariable) { throw new ArgumentException(SR.GetString(SR.ListBoxVarHeightMultiCol), "value"); } drawMode = value; RecreateHandle(); if (drawMode == DrawMode.OwnerDrawVariable) { // VSWhidbey 139179 - force a layout after RecreateHandle() completes because now // the LB is definitely fully populated and can report a preferred size accurately. LayoutTransaction.DoLayoutIf(AutoSize, this.ParentInternal, this, PropertyNames.DrawMode); } } } } // Used internally to find the currently focused item // internal int FocusedIndex { get { if (IsHandleCreated) { return (int)SendMessage(NativeMethods.LB_GETCARETINDEX, 0, 0); } return -1; } } ///// VSWhidbey 95179: The scroll bars don't display properly when the IntegralHeight == false // and the control is resized before the font size is change and the new font size causes // the height of all the items to exceed the new height of the control. This is a bug in // the control, but can be easily worked around by removing and re-adding all the items. public override Font Font { get { return base.Font; } set { base.Font = value; if (false == integralHeight) { // VSWhidbey 95179: Refresh the list to force the scroll bars to display // when the integral height is false. RefreshItems(); } } } /// public override Color ForeColor { get { if (ShouldSerializeForeColor()) { return base.ForeColor; } else { return SystemColors.WindowText; } } set { base.ForeColor = value; } } /// /// /// Indicates the width, in pixels, by which a list box can be scrolled horizontally (the scrollable width). /// This property will only have an effect if HorizontalScrollbars is true. /// [ SRCategory(SR.CatBehavior), DefaultValue(0), Localizable(true), SRDescription(SR.ListBoxHorizontalExtentDescr) ] public int HorizontalExtent { get { return horizontalExtent; } set { if (value != horizontalExtent) { horizontalExtent = value; UpdateHorizontalExtent(); } } } ////// /// Indicates whether or not the ListBox should display a horizontal scrollbar /// when the items extend beyond the right edge of the ListBox. /// If true, the scrollbar will automatically set its extent depending on the length /// of items in the ListBox. The exception is if the ListBox is owner-draw, in /// which case HorizontalExtent will need to be explicitly set. /// [ SRCategory(SR.CatBehavior), DefaultValue(false), Localizable(true), SRDescription(SR.ListBoxHorizontalScrollbarDescr) ] public bool HorizontalScrollbar { get { return horizontalScrollbar; } set { if (value != horizontalScrollbar) { horizontalScrollbar = value; // There seems to be a bug in the native ListBox in that the addition // of the horizontal scroll bar does not get reflected in the control // rightaway. So, we refresh the items here. RefreshItems(); // Only need to recreate the handle if not MultiColumn // (HorizontalScrollbar has no effect on a MultiColumn listbox) // if (!MultiColumn) { RecreateHandle(); } } } } ////// /// Indicates if the listbox should avoid showing partial Items. If so, /// then only full items will be displayed, and the listbox will be resized /// to prevent partial items from being shown. Otherwise, they will be /// shown /// [ SRCategory(SR.CatBehavior), DefaultValue(true), Localizable(true), SRDescription(SR.ListBoxIntegralHeightDescr), RefreshProperties(RefreshProperties.Repaint) ] public bool IntegralHeight { get { return integralHeight; } set { if (integralHeight != value) { integralHeight = value; RecreateHandle(); // Avoid the listbox and textbox behaviour in Collection editors // integralHeightAdjust = true; try { Height = requestedHeight; } finally { integralHeightAdjust = false; } } } } ////// /// [ SRCategory(SR.CatBehavior), DefaultValue(DefaultItemHeight), Localizable(true), SRDescription(SR.ListBoxItemHeightDescr), RefreshProperties(RefreshProperties.Repaint) ] public virtual int ItemHeight { get { if (drawMode == DrawMode.OwnerDrawFixed || drawMode == DrawMode.OwnerDrawVariable) { return itemHeight; } return GetItemHeight(0); } set { if (value < 1 || value > 255) { throw new ArgumentOutOfRangeException("ItemHeight", SR.GetString(SR.InvalidExBoundArgument, "ItemHeight", (value).ToString(CultureInfo.CurrentCulture), (0).ToString(CultureInfo.CurrentCulture), "256")); } if (itemHeight != value) { itemHeight = value; if (drawMode == DrawMode.OwnerDrawFixed && IsHandleCreated) { BeginUpdate(); SendMessage(NativeMethods.LB_SETITEMHEIGHT, 0, value); // Changing the item height might require a resize for IntegralHeight list boxes // if (IntegralHeight) { Size oldSize = Size; Size = new Size(oldSize.Width + 1, oldSize.Height); Size = oldSize; } EndUpdate(); } } } } ////// Returns /// the height of an item in an owner-draw list box. ////// /// Collection of items in this listbox. /// [ SRCategory(SR.CatData), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), Localizable(true), SRDescription(SR.ListBoxItemsDescr), Editor("System.Windows.Forms.Design.ListControlStringCollectionEditor, " + AssemblyRef.SystemDesign, typeof(UITypeEditor)), MergableProperty(false) ] public ObjectCollection Items { get { if (itemsCollection == null) { itemsCollection = CreateItemCollection(); } return itemsCollection; } } // Computes the maximum width of all items in the ListBox // internal virtual int MaxItemWidth { get { if (horizontalExtent > 0) { return horizontalExtent; } if (DrawMode != DrawMode.Normal) { return -1; } // Return cached maxWidth if available // if (maxWidth > -1) { return maxWidth; } // Compute maximum width // maxWidth = ComputeMaxItemWidth(maxWidth); return maxWidth; } } ////// /// [ SRCategory(SR.CatBehavior), DefaultValue(false), SRDescription(SR.ListBoxMultiColumnDescr) ] public bool MultiColumn { get { return multiColumn; } set { if (multiColumn != value) { if (value && drawMode == DrawMode.OwnerDrawVariable) { throw new ArgumentException(SR.GetString(SR.ListBoxVarHeightMultiCol), "value"); } multiColumn = value; RecreateHandle(); } } } ////// Indicates if the listbox is multi-column /// or not. ////// /// The total height of the items in the list box. /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.ListBoxPreferredHeightDescr) ] public int PreferredHeight { get { int height = 0; if (drawMode == DrawMode.OwnerDrawVariable) { // VSWhidbey 139179 - don't try to get item heights from the LB when items haven't been // added to the LB yet. Just return current height. if (RecreatingHandle || GetState(STATE_CREATINGHANDLE)) { height = this.Height; } else { if (itemsCollection != null) { int cnt = itemsCollection.Count; for (int i = 0; i < cnt; i++) { height += GetItemHeight(i); } } } } else { //VSWhidbey #148270 //When the list is empty, we don't want to multiply by 0 here. int cnt = (itemsCollection == null || itemsCollection.Count == 0) ? 1 : itemsCollection.Count; height = GetItemHeight(0) * cnt; } if (borderStyle != BorderStyle.None) { height += SystemInformation.BorderSize.Height * 4 + 3; } return height; } } internal override Size GetPreferredSizeCore(Size proposedConstraints) { int height = PreferredHeight; int width; // Convert with a dummy height to add space required for borders // VSWhidbey #151141 -PreferredSize should return either the new // size of the control, or the default size if the handle has not been // created if (IsHandleCreated) { width = SizeFromClientSize(new Size(MaxItemWidth, height)).Width; width += SystemInformation.VerticalScrollBarWidth + 4; } else { return DefaultSize; } return new Size(width, height) + Padding.Size; } ///public override RightToLeft RightToLeft { get { if (!RunningOnWin2K) { return RightToLeft.No; } return base.RightToLeft; } set { base.RightToLeft = value; } } static bool RunningOnWin2K { get { if (!checkedOS) { if (Environment.OSVersion.Platform != System.PlatformID.Win32NT || Environment.OSVersion.Version.Major < 5) { runningOnWin2K = false; checkedOS = true; } } return runningOnWin2K; } } /// /// /// [ SRCategory(SR.CatBehavior), DefaultValue(false), Localizable(true), SRDescription(SR.ListBoxScrollIsVisibleDescr) ] public bool ScrollAlwaysVisible { get { return scrollAlwaysVisible; } set { if (scrollAlwaysVisible != value) { scrollAlwaysVisible = value; RecreateHandle(); } } } ////// Gets or sets whether the scrollbar is shown at all times. ////// /// Indicates whether list currently allows selection of list items. /// For ListBox, this returns true unless SelectionMode is SelectionMode.None. /// protected override bool AllowSelection { get { return selectionMode != SelectionMode.None; } } ////// /// The index of the currently selected item in the list, if there /// is one. If the value is -1, there is currently no selection. If the /// value is 0 or greater, than the value is the index of the currently /// selected item. If the MultiSelect property on the ListBox is true, /// then a non-zero value for this property is the index of the first /// selection /// [ Browsable(false), Bindable(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.ListBoxSelectedIndexDescr) ] public override int SelectedIndex { get { SelectionMode current = (selectionModeChanging) ? cachedSelectionMode : selectionMode; if (current == SelectionMode.None) { return -1; } if (current == SelectionMode.One && IsHandleCreated) { return (int)SendMessage(NativeMethods.LB_GETCURSEL, 0, 0); } if (itemsCollection != null && SelectedItems.Count > 0) { return Items.IndexOfIdentifier(SelectedItems.GetObjectAt(0)); } return -1; } set { int itemCount = (itemsCollection == null) ? 0 : itemsCollection.Count; if (value < -1 || value >= itemCount) { throw new ArgumentOutOfRangeException("SelectedIndex", SR.GetString(SR.InvalidArgument, "SelectedIndex", (value).ToString(CultureInfo.CurrentCulture))); } if (selectionMode == SelectionMode.None) { throw new ArgumentException(SR.GetString(SR.ListBoxInvalidSelectionMode), "SelectedIndex"); } if (selectionMode == SelectionMode.One && value != -1) { // Single select an individual value. int currentIndex = SelectedIndex; if (currentIndex != value) { if (currentIndex != -1) { SelectedItems.SetSelected(currentIndex, false); } SelectedItems.SetSelected(value, true); if (IsHandleCreated) { NativeSetSelected(value, true); } OnSelectedIndexChanged(EventArgs.Empty); } } else if (value == -1) { if (SelectedIndex != -1) { ClearSelected(); // ClearSelected raises OnSelectedIndexChanged for us } } else { if (!SelectedItems.GetSelected(value)) { // Select this item while keeping any previously selected items selected. // SelectedItems.SetSelected(value, true); if (IsHandleCreated) { NativeSetSelected(value, true); } OnSelectedIndexChanged(EventArgs.Empty); } } } } ////// /// A collection of the indices of the selected items in the /// list box. If there are no selected items in the list box, the result is /// an empty collection. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.ListBoxSelectedIndicesDescr) ] public SelectedIndexCollection SelectedIndices { get { if (selectedIndices == null) { selectedIndices = new SelectedIndexCollection(this); } return selectedIndices; } } ////// /// The value of the currently selected item in the list, if there /// is one. If the value is null, there is currently no selection. If the /// value is non-null, then the value is that of the currently selected /// item. If the MultiSelect property on the ListBox is true, then a /// non-null return value for this method is the value of the first item /// selected /// [ Browsable(false), Bindable(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.ListBoxSelectedItemDescr) ] public object SelectedItem { get { if (SelectedItems.Count > 0) { return SelectedItems[0]; } return null; } set { if (itemsCollection != null) { if (value != null) { int index = itemsCollection.IndexOf(value); if (index != -1) { SelectedIndex = index; } } else { SelectedIndex = -1; } } } } ////// /// The collection of selected items. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.ListBoxSelectedItemsDescr) ] public SelectedObjectCollection SelectedItems { get { if (selectedItems == null) { selectedItems = new SelectedObjectCollection(this); } return selectedItems; } } ////// /// Controls how many items at a time can be selected in the listbox. Valid /// values are from the System.Windows.Forms.SelectionMode enumeration. /// [ SRCategory(SR.CatBehavior), DefaultValue(SelectionMode.One), SRDescription(SR.ListBoxSelectionModeDescr) ] public virtual SelectionMode SelectionMode { get { return selectionMode; } set { if (!ClientUtils.IsEnumValid(value, (int)value, (int)SelectionMode.None, (int)SelectionMode.MultiExtended)) { throw new InvalidEnumArgumentException("value", (int)value, typeof(SelectionMode)); } if (selectionMode != value) { SelectedItems.EnsureUpToDate(); selectionMode = value; try { selectionModeChanging = true; RecreateHandle(); } finally { selectionModeChanging = false; cachedSelectionMode = selectionMode; // update the selectedItems list and SelectedItems index collection if (IsHandleCreated) { NativeUpdateSelection(); } } } } } ////// /// Indicates if the ListBox is sorted or not. 'true' means that strings in /// the list will be sorted alphabetically /// [ SRCategory(SR.CatBehavior), DefaultValue(false), SRDescription(SR.ListBoxSortedDescr) ] public bool Sorted { get { return sorted; } set { if (sorted != value) { sorted = value; if (sorted && itemsCollection != null && itemsCollection.Count >= 1) { Sort(); } } } } ////// /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Bindable(false) ] public override string Text { get { if (SelectionMode != SelectionMode.None && SelectedItem != null) { if (FormattingEnabled) { return GetItemText(SelectedItem); } else { return FilterItemOnProperty(SelectedItem).ToString(); } } else { return base.Text; } } set { base.Text = value; // Scan through the list items looking for the supplied text string. If we find it, // select it. // if (SelectionMode != SelectionMode.None && value != null && (SelectedItem == null || !value.Equals(GetItemText(SelectedItem)))) { int cnt = Items.Count; for (int index=0; index < cnt; ++index) { if (String.Compare(value, GetItemText(Items[index]), true, CultureInfo.CurrentCulture) == 0) { SelectedIndex = index; return; } } } } } ///[To be supplied.] ////// [Browsable(false), EditorBrowsable(EditorBrowsableState.Advanced)] new public event EventHandler TextChanged { add { base.TextChanged += value; } remove { base.TextChanged -= value; } } /// /// /// The index of the first visible item in a list box. Initially /// the item with index 0 is at the top of the list box, but if the list /// box contents have been scrolled another item may be at the top. /// [ Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), SRDescription(SR.ListBoxTopIndexDescr) ] public int TopIndex { get { if (IsHandleCreated) { return (int)SendMessage(NativeMethods.LB_GETTOPINDEX, 0, 0); } else { return topIndex; } } set { if (IsHandleCreated) { SendMessage(NativeMethods.LB_SETTOPINDEX, value, 0); } else { topIndex = value; } } } ////// /// Enables a list box to recognize and expand tab characters when drawing /// its strings. /// [ SRCategory(SR.CatBehavior), DefaultValue(true), SRDescription(SR.ListBoxUseTabStopsDescr) ] public bool UseTabStops { get { return useTabStops; } set { if (useTabStops != value) { useTabStops = value; RecreateHandle(); } } } ////// /// Allows to set the width of the tabs between the items in the list box. /// The integer array should have the tab spaces in the ascending order. /// [ SRCategory(SR.CatBehavior), SRDescription(SR.ListBoxCustomTabOffsetsDescr), DesignerSerializationVisibility(DesignerSerializationVisibility.Content), Browsable(false) ] public IntegerCollection CustomTabOffsets { get { if (customTabOffsets == null) { customTabOffsets = new IntegerCollection(this); } return customTabOffsets; } } ////// /// Performs the work of adding the specified items to the Listbox /// [Obsolete("This method has been deprecated. There is no replacement. http://go.microsoft.com/fwlink/?linkid=14202")] protected virtual void AddItemsCore(object[] value) { int count = value == null? 0: value.Length; if (count == 0) { return; } Items.AddRangeInternal(value); } ///[Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] public new event EventHandler Click { add { base.Click += value; } remove { base.Click -= value; } } /// [Browsable(true), EditorBrowsable(EditorBrowsableState.Always)] public new event MouseEventHandler MouseClick { add { base.MouseClick += value; } remove { base.MouseClick -= value; } } /// /// /// [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden) ] public new Padding Padding { get { return base.Padding; } set { base.Padding = value;} } [ Browsable(false), EditorBrowsable(EditorBrowsableState.Never) ] public new event EventHandler PaddingChanged { add { base.PaddingChanged += value; } remove { base.PaddingChanged -= value; } } ////// ///[To be supplied.] ////// /// ListBox / CheckedListBox Onpaint. /// [Browsable(false), EditorBrowsable(EditorBrowsableState.Never)] public new event PaintEventHandler Paint { add { base.Paint += value; } remove { base.Paint -= value; } } ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.drawItemEventDescr)] public event DrawItemEventHandler DrawItem { add { Events.AddHandler(EVENT_DRAWITEM, value); } remove { Events.RemoveHandler(EVENT_DRAWITEM, value); } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.measureItemEventDescr)] public event MeasureItemEventHandler MeasureItem { add { Events.AddHandler(EVENT_MEASUREITEM, value); } remove { Events.RemoveHandler(EVENT_MEASUREITEM, value); } } ///[To be supplied.] ////// /// [SRCategory(SR.CatBehavior), SRDescription(SR.selectedIndexChangedEventDescr)] public event EventHandler SelectedIndexChanged { add { Events.AddHandler(EVENT_SELECTEDINDEXCHANGED, value); } remove { Events.RemoveHandler(EVENT_SELECTEDINDEXCHANGED, value); } } ///[To be supplied.] ////// /// While the preferred way to insert items is to set Items.All, /// and set all the items at once, there are times when you may wish to /// insert each item one at a time. To help with the performance of this, /// it is desirable to prevent the ListBox from painting during these /// operations. This method, along with EndUpdate, is the preferred /// way of doing this. Don't forget to call EndUpdate when you're done, /// or else the ListBox won't paint properly afterwards. /// public void BeginUpdate() { BeginUpdateInternal(); updateCount++; } private void CheckIndex(int index) { if (index < 0 || index >= Items.Count) throw new ArgumentOutOfRangeException("index", SR.GetString(SR.IndexOutOfRange, index.ToString(CultureInfo.CurrentCulture))); } private void CheckNoDataSource() { if (DataSource != null) throw new ArgumentException(SR.GetString(SR.DataSourceLocksItems)); } ////// /// protected virtual ObjectCollection CreateItemCollection() { return new ObjectCollection(this); } internal virtual int ComputeMaxItemWidth(int oldMax) { // pass LayoutUtils the collection of strings string[] strings = new string[this.Items.Count]; for (int i = 0; i < Items.Count; i ++) { strings[i] = GetItemText(Items[i]); } Size textSize = LayoutUtils.OldGetLargestStringSizeInCollection(Font, strings); return Math.Max(oldMax, textSize.Width); } ///[To be supplied.] ////// /// Unselects all currently selected items. /// public void ClearSelected() { bool hadSelection = false; int itemCount = (itemsCollection == null) ? 0 : itemsCollection.Count; for (int x = 0; x < itemCount;x++) { if (SelectedItems.GetSelected(x)) { hadSelection = true; SelectedItems.SetSelected(x, false); if (IsHandleCreated) { NativeSetSelected(x, false); } } } if (hadSelection) { OnSelectedIndexChanged(EventArgs.Empty); } } ////// /// While the preferred way to insert items is to set Items.All, /// and set all the items at once, there are times when you may wish to /// insert each item one at a time. To help with the performance of this, /// it is desirable to prevent the ListBox from painting during these /// operations. This method, along with BeginUpdate, is the preferred /// way of doing this. BeginUpdate should be called first, and this method /// should be called when you want the control to start painting again. /// public void EndUpdate() { EndUpdateInternal(); --updateCount; } ////// /// Finds the first item in the list box that starts with the given string. /// The search is not case sensitive. /// public int FindString(string s) { return FindString(s, -1); } ////// /// Finds the first item after the given index which starts with the given /// string. The search is not case sensitive. /// public int FindString(string s, int startIndex) { if (s == null) return -1; int itemCount = (itemsCollection == null) ? 0 : itemsCollection.Count; if (itemCount == 0) { return -1; } // VSWhidbey 95158: The last item in the list is still a valid starting point for a search. if (startIndex < -1 || startIndex >= itemCount) { throw new ArgumentOutOfRangeException("startIndex"); } // Always use the managed FindStringInternal instead of LB_FINDSTRING. // The managed version correctly handles Turkish I. return FindStringInternal(s, Items, startIndex, false); } ////// /// Finds the first item in the list box that matches the given string. /// The strings must match exactly, except for differences in casing. /// public int FindStringExact(string s) { return FindStringExact(s, -1); } ////// /// Finds the first item after the given index that matches the given /// string. The strings must match excatly, except for differences in /// casing. /// public int FindStringExact(string s, int startIndex) { if (s == null) return -1; int itemCount = (itemsCollection == null) ? 0 : itemsCollection.Count; if (itemCount == 0) { return -1; } // VSWhidbey 95158: The last item in the list is still a valid starting point for a search. if (startIndex < -1 || startIndex >= itemCount) { throw new ArgumentOutOfRangeException("startIndex"); } // Always use the managed FindStringInternal instead of LB_FINDSTRING. // The managed version correctly handles Turkish I. // return FindStringInternal(s, Items, startIndex, true); } ////// /// Returns the height of the given item in a list box. The index parameter /// is ignored if drawMode is not OwnerDrawVariable. /// public int GetItemHeight(int index) { int itemCount = (itemsCollection == null) ? 0 : itemsCollection.Count; // Note: index == 0 is OK even if the ListBox currently has // no items. // if (index < 0 || (index > 0 && index >= itemCount)) throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); if (drawMode != DrawMode.OwnerDrawVariable) index = 0; if (IsHandleCreated) { int h = (int)SendMessage(NativeMethods.LB_GETITEMHEIGHT, index, 0); if (h == -1) throw new Win32Exception(); return h; } return itemHeight; } ////// /// Retrieves a Rectangle object which describes the bounding rectangle /// around an item in the list. If the item in question is not visible, /// the rectangle will be outside the visible portion of the control. /// public Rectangle GetItemRectangle(int index) { CheckIndex(index); NativeMethods.RECT rect = new NativeMethods.RECT(); SendMessage(NativeMethods.LB_GETITEMRECT, index, ref rect); return Rectangle.FromLTRB(rect.left, rect.top, rect.right, rect.bottom); } ////// List box overrides GetScaledBounds to ensure we always scale the requested /// height, not the current height. /// [EditorBrowsable(EditorBrowsableState.Advanced)] protected override Rectangle GetScaledBounds(Rectangle bounds, SizeF factor, BoundsSpecified specified) { // update bounds' height to use the requested height, not the current height. These // can be different if integral height is turned on. bounds.Height = requestedHeight; return base.GetScaledBounds(bounds, factor, specified); } ////// /// Tells you whether or not the item at the supplied index is selected /// or not. /// public bool GetSelected(int index) { CheckIndex(index); return GetSelectedInternal(index); } private bool GetSelectedInternal(int index) { if (IsHandleCreated) { int sel = (int)SendMessage(NativeMethods.LB_GETSEL, index, 0); if (sel == -1) { throw new Win32Exception(); } return sel > 0; } else { if (itemsCollection != null && SelectedItems.GetSelected(index)) { return true; } return false; } } ////// /// Retrieves the index of the item at the given coordinates. /// public int IndexFromPoint(Point p) { return IndexFromPoint(p.X, p.Y); } ////// /// Retrieves the index of the item at the given coordinates. /// public int IndexFromPoint(int x, int y) { //NT4 SP6A : SendMessage Fails. So First check whether the point is in Client Co-ordinates and then //call Sendmessage. // NativeMethods.RECT r = new NativeMethods.RECT(); UnsafeNativeMethods.GetClientRect(new HandleRef(this, Handle), ref r); if (r.left <= x && x < r.right && r.top <= y && y < r.bottom) { int index = (int)SendMessage(NativeMethods.LB_ITEMFROMPOINT, 0, (int)NativeMethods.Util.MAKELPARAM(x, y)); if (NativeMethods.Util.HIWORD(index) == 0) { // Inside ListBox client area return NativeMethods.Util.LOWORD(index); } } return NoMatches; } ////// Adds the given item to the native combo box. This asserts if the handle hasn't been /// created. /// private int NativeAdd(object item) { Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); int insertIndex = (int)SendMessage(NativeMethods.LB_ADDSTRING, 0, GetItemText(item)); if (insertIndex == NativeMethods.LB_ERRSPACE) { throw new OutOfMemoryException(); } if (insertIndex == NativeMethods.LB_ERR) { // On some platforms (e.g. Win98), the ListBox control // appears to return LB_ERR if there are a large number (>32000) // of items. It doesn't appear to set error codes appropriately, // so we'll have to assume that LB_ERR corresponds to item // overflow. // throw new OutOfMemoryException(SR.GetString(SR.ListBoxItemOverflow)); } return insertIndex; } ////// Clears the contents of the combo box. /// private void NativeClear() { Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); SendMessage(NativeMethods.LB_RESETCONTENT, 0, 0); } ////// Get the text stored by the native control for the specified list item. /// internal string NativeGetItemText(int index) { int len = (int) SendMessage(NativeMethods.LB_GETTEXTLEN, index, 0); StringBuilder sb = new StringBuilder(len + 1); UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LB_GETTEXT, index, sb); return sb.ToString(); } ////// Inserts the given item to the native combo box at the index. This asserts if the handle hasn't been /// created or if the resulting insert index doesn't match the passed in index. /// private int NativeInsert(int index, object item) { Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); int insertIndex = (int)SendMessage(NativeMethods.LB_INSERTSTRING, index, GetItemText(item)); if (insertIndex == NativeMethods.LB_ERRSPACE) { throw new OutOfMemoryException(); } if (insertIndex == NativeMethods.LB_ERR) { // On some platforms (e.g. Win98), the ListBox control // appears to return LB_ERR if there are a large number (>32000) // of items. It doesn't appear to set error codes appropriately, // so we'll have to assume that LB_ERR corresponds to item // overflow. // throw new OutOfMemoryException(SR.GetString(SR.ListBoxItemOverflow)); } Debug.Assert(insertIndex == index, "NativeListBox inserted at " + insertIndex + " not the requested index of " + index); return insertIndex; } ////// Removes the native item from the given index. /// private void NativeRemoveAt(int index) { Debug.Assert(IsHandleCreated, "Shouldn't be calling Native methods before the handle is created."); bool selected = ((int)SendMessage(NativeMethods.LB_GETSEL, (IntPtr)index, IntPtr.Zero) > 0); SendMessage(NativeMethods.LB_DELETESTRING, index, 0); //If the item currently selected is removed then we should fire a Selectionchanged event... //as the next time selected index returns -1... if (selected) { OnSelectedIndexChanged(EventArgs.Empty); } } ////// Sets the selection of the given index to the native window. This does not change /// the collection; you must update the collection yourself. /// private void NativeSetSelected(int index, bool value) { Debug.Assert(IsHandleCreated, "Should only call Native methods after the handle has been created"); Debug.Assert(selectionMode != SelectionMode.None, "Guard against setting selection for None selection mode outside this code."); if (selectionMode == SelectionMode.One) { SendMessage(NativeMethods.LB_SETCURSEL, (value ? index : -1), 0); } else { SendMessage(NativeMethods.LB_SETSEL, value? -1: 0, index); } } ////// This is called by the SelectedObjectCollection in response to the first /// query on that collection after we have called Dirty(). Dirty() is called /// when we receive a LBN_SELCHANGE message. /// private void NativeUpdateSelection() { Debug.Assert(IsHandleCreated, "Should only call native methods if handle is created"); // Clear the selection state. // int cnt = Items.Count; for (int i = 0; i < cnt; i++) { SelectedItems.SetSelected(i, false); } int[] result = null; switch (selectionMode) { case SelectionMode.One: int index = (int)SendMessage(NativeMethods.LB_GETCURSEL, 0, 0); if (index >= 0) result = new int[] {index}; break; case SelectionMode.MultiSimple: case SelectionMode.MultiExtended: int count = (int)SendMessage(NativeMethods.LB_GETSELCOUNT, 0, 0); if (count > 0) { result = new int[count]; UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LB_GETSELITEMS, count, result); } break; } // Now set the selected state on the appropriate items. // if (result != null) { foreach(int i in result) { SelectedItems.SetSelected(i, true); } } } ////// /// protected override void OnChangeUICues(UICuesEventArgs e) { // ListBox seems to get a bit confused when the UI cues change for the first // time - it draws the focus rect when it shouldn't and vice-versa. So when // the UI cues change, we just do an extra invalidate to get it into the // right state. // Invalidate(); base.OnChangeUICues(e); } ///[To be supplied.] ////// /// Actually goes and fires the drawItem event. Inheriting controls /// should use this to know when the event is fired [this is preferable to /// adding an event handler yourself for this event]. They should, /// however, remember to call base.onDrawItem(e); to ensure the event is /// still fired to external listeners /// protected virtual void OnDrawItem(DrawItemEventArgs e) { DrawItemEventHandler handler = (DrawItemEventHandler)Events[EVENT_DRAWITEM]; if (handler != null) { handler(this, e); } } ////// /// We need to know when the window handle has been created so we can /// set up a few things, like column width, etc! Inheriting classes should /// not forget to call base.OnHandleCreated(). /// protected override void OnHandleCreated(EventArgs e) { base.OnHandleCreated(e); //for getting the current Locale to set the Scrollbars... // SendMessage(NativeMethods.LB_SETLOCALE, CultureInfo.CurrentCulture.LCID, 0); if (columnWidth != 0) { SendMessage(NativeMethods.LB_SETCOLUMNWIDTH, columnWidth, 0); } if (drawMode == DrawMode.OwnerDrawFixed) { SendMessage(NativeMethods.LB_SETITEMHEIGHT, 0, ItemHeight); } if (topIndex != 0) { SendMessage(NativeMethods.LB_SETTOPINDEX, topIndex, 0); } if (UseCustomTabOffsets && CustomTabOffsets != null) { int wpar = CustomTabOffsets.Count; int[] offsets = new int[wpar]; CustomTabOffsets.CopyTo(offsets, 0); UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LB_SETTABSTOPS, wpar, offsets); } if (itemsCollection != null) { int count = itemsCollection.Count; for(int i = 0; i < count; i++) { NativeAdd(itemsCollection[i]); if (selectionMode != SelectionMode.None) { if (selectedItems != null) { selectedItems.PushSelectionIntoNativeListBox(i); } } } } if (selectedItems != null) { if (selectedItems.Count > 0 && selectionMode == SelectionMode.One) { SelectedItems.Dirty(); SelectedItems.EnsureUpToDate(); } } UpdateHorizontalExtent(); } ////// /// Overridden to make sure that we set up and clear out items /// correctly. Inheriting controls should not forget to call /// base.OnHandleDestroyed() /// protected override void OnHandleDestroyed(EventArgs e) { SelectedItems.EnsureUpToDate(); if (Disposing) { itemsCollection = null; } base.OnHandleDestroyed(e); } ////// /// protected virtual void OnMeasureItem(MeasureItemEventArgs e) { MeasureItemEventHandler handler = (MeasureItemEventHandler)Events[EVENT_MEASUREITEM]; if (handler != null) { handler(this, e); } } ///[To be supplied.] ////// /// protected override void OnFontChanged(EventArgs e) { base.OnFontChanged(e); // Changing the font causes us to resize, always rounding down. // Make sure we do this after base.OnPropertyChanged, which sends the WM_SETFONT message // Avoid the listbox and textbox behaviour in Collection editors // UpdateFontCache(); } ///[To be supplied.] ////// /// protected override void OnParentChanged(EventArgs e) { base.OnParentChanged(e); //No need to RecreateHandle if we are removing the Listbox from controls collection... //so check the parent before recreating the handle... if (this.ParentInternal != null) { RecreateHandle(); } } ///We override this so we can re-create the handle if the parent has changed. ////// /// protected override void OnResize(EventArgs e) { base.OnResize(e); // There are some repainting issues for RightToLeft - so invalidate when we resize. // if (RightToLeft == RightToLeft.Yes || this.HorizontalScrollbar) { Invalidate(); } } ///[To be supplied.] ////// /// Actually goes and fires the selectedIndexChanged event. Inheriting controls /// should use this to know when the event is fired [this is preferable to /// adding an event handler on yourself for this event]. They should, /// however, remember to call base.OnSelectedIndexChanged(e); to ensure the event is /// still fired to external listeners /// protected override void OnSelectedIndexChanged(EventArgs e) { base.OnSelectedIndexChanged(e); // set the position in the dataSource, if there is any // we will only set the position in the currencyManager if it is different // from the SelectedIndex. Setting CurrencyManager::Position (even w/o changing it) // calls CurrencyManager::EndCurrentEdit, and that will pull the dataFrom the controls // into the backEnd. We do not need to do that. // if (this.DataManager != null && DataManager.Position != SelectedIndex) { //read this as "if everett or (whidbey and selindex is valid)" if (!FormattingEnabled || this.SelectedIndex != -1) { // VSWhidbey 95176: don't change dataManager position if we simply unselected everything. // (Doing so would cause the first LB item to be selected...) this.DataManager.Position = this.SelectedIndex; } } // VSWhidbey 163411: Call the handler after updating the DataManager's position so that // the DataManager's selected index will be correct in an event handler. EventHandler handler = (EventHandler)Events[EVENT_SELECTEDINDEXCHANGED]; if (handler != null) { handler(this, e); } } ///protected override void OnSelectedValueChanged(EventArgs e) { base.OnSelectedValueChanged(e); selectedValueChangedFired = true; } /// protected override void OnDataSourceChanged(EventArgs e) { if (DataSource == null) { BeginUpdate(); SelectedIndex = -1; Items.ClearInternal(); EndUpdate(); } base.OnDataSourceChanged(e); RefreshItems(); } /// protected override void OnDisplayMemberChanged(EventArgs e) { base.OnDisplayMemberChanged(e); // we want to use the new DisplayMember even if there is no data source RefreshItems(); if (SelectionMode != SelectionMode.None && this.DataManager != null) this.SelectedIndex = this.DataManager.Position; } /// /// /// Forces the ListBox to invalidate and immediately /// repaint itself and any children if OwnerDrawVariable. /// public override void Refresh() { if (drawMode == DrawMode.OwnerDrawVariable) { //Fire MeasureItem for Each Item in the Listbox... int cnt = Items.Count; Graphics graphics = CreateGraphicsInternal(); try { for (int i = 0; i < cnt; i++) { MeasureItemEventArgs mie = new MeasureItemEventArgs(graphics, i, ItemHeight); OnMeasureItem(mie); } } finally { graphics.Dispose(); } } base.Refresh(); } ////// /// Reparses the objects, getting new text strings for them. /// ///protected override void RefreshItems() { // Store the currently selected object collection. // ObjectCollection savedItems = itemsCollection; // Clear the items. // itemsCollection = null; selectedIndices = null; if (IsHandleCreated) { NativeClear(); } object[] newItems = null; // if we have a dataSource and a DisplayMember, then use it // to populate the Items collection // if (this.DataManager != null && this.DataManager.Count != -1) { newItems = new object[this.DataManager.Count]; for(int i = 0; i < newItems.Length; i++) { newItems[i] = this.DataManager[i]; } } else if (savedItems != null) { newItems = new object[savedItems.Count]; savedItems.CopyTo(newItems, 0); } // Store the current list of items // if (newItems != null) { Items.AddRangeInternal(newItems); } // Restore the selected indices if SelectionMode allows it. // if (SelectionMode != SelectionMode.None) { if (this.DataManager != null) { // put the selectedIndex in [....] w/ the position in the dataManager this.SelectedIndex = this.DataManager.Position; } else { if (savedItems != null) { int cnt = savedItems.Count; for(int index = 0; index < cnt; index++) { if (savedItems.InnerArray.GetState(index, SelectedObjectCollection.SelectedObjectMask)) { SelectedItem = savedItems[index]; } } } } } } /// /// /// Reparses the object at the given index, getting new text string for it. /// ///protected override void RefreshItem(int index) { Items.SetItemInternal(index, Items[index]); } public override void ResetBackColor() { base.ResetBackColor(); } public override void ResetForeColor() { base.ResetForeColor(); } private void ResetItemHeight() { itemHeight = DefaultItemHeight; } [SuppressMessage("Microsoft.Portability", "CA1902:AvoidTestingForFloatingPointEquality")] protected override void ScaleControl(SizeF factor, BoundsSpecified specified) { if (factor.Width != 1F && factor.Height != 1F) { UpdateFontCache(); } base.ScaleControl(factor, specified); } /// /// /// Overrides Control.SetBoundsCore to remember the requestedHeight. /// ///protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) { // Avoid the listbox and textbox behaviour in Collection editors // if (!integralHeightAdjust && height != Height) requestedHeight = height; base.SetBoundsCore(x, y, width, height, specified); } /// /// /// Performs the work of setting the specified items into the ListBox. /// protected override void SetItemsCore(IList value) { BeginUpdate(); Items.ClearInternal(); Items.AddRangeInternal(value); this.SelectedItems.Dirty(); // if the list changed, we want to keep the same selected index // CurrencyManager will provide the PositionChanged event // it will be provided before changing the list though... if (this.DataManager != null) { if (this.DataSource is ICurrencyManagerProvider) { // Everett ListControl's had a bug where they would not fire // OnSelectedValueChanged if their list of items were refreshed. // We fix this post-Everett. // However, for APPCOMPAT reasons, we only want to fix it when binding to // Whidbey components. // vsw 547279. this.selectedValueChangedFired = false; } if (IsHandleCreated) { SendMessage(NativeMethods.LB_SETCURSEL, DataManager.Position, 0); } // if the list changed and we still did not fire the // onselectedChanged event, then fire it now; if (!selectedValueChangedFired) { OnSelectedValueChanged(EventArgs.Empty); selectedValueChangedFired = false; } } EndUpdate(); } ///protected override void SetItemCore(int index, object value) { Items.SetItemInternal(index, value); } /// /// /// Allows the user to set an item as being selected or not. This should /// only be used with ListBoxes that allow some sort of multi-selection. /// public void SetSelected(int index, bool value) { int itemCount = (itemsCollection == null) ? 0: itemsCollection.Count; if (index < 0 || index >= itemCount) throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); if (selectionMode == SelectionMode.None) throw new InvalidOperationException(SR.GetString(SR.ListBoxInvalidSelectionMode)); SelectedItems.SetSelected(index, value); if (IsHandleCreated) { NativeSetSelected(index, value); } SelectedItems.Dirty(); OnSelectedIndexChanged(EventArgs.Empty); } ////// /// Sorts the items in the listbox. /// protected virtual void Sort() { // This will force the collection to add each item back to itself // if sorted is now true, then the add method will insert the item // into the correct position // CheckNoDataSource(); SelectedObjectCollection currentSelections = SelectedItems; currentSelections.EnsureUpToDate(); if (sorted && itemsCollection != null) { itemsCollection.InnerArray.Sort(); // Now that we've sorted, update our handle // if it has been created. if (IsHandleCreated) { NativeClear(); int count = itemsCollection.Count; for(int i = 0; i < count; i++) { NativeAdd(itemsCollection[i]); if (currentSelections.GetSelected(i)) { NativeSetSelected(i, true); } } } } } ////// /// Returns a string representation for this control. /// ///public override string ToString() { string s = base.ToString(); if (itemsCollection != null) { s += ", Items.Count: " + Items.Count.ToString(CultureInfo.CurrentCulture); if (Items.Count > 0) { string z = GetItemText(Items[0]); string txt = (z.Length > 40) ? z.Substring(0, 40) : z; s += ", Items[0]: " + txt; } } return s; } private void UpdateFontCache() { fontIsChanged = true; integralHeightAdjust = true; try { Height = requestedHeight; } finally { integralHeightAdjust = false; } maxWidth = -1; UpdateHorizontalExtent(); // clear the preferred size cache. CommonProperties.xClearPreferredSizeCache(this); } private void UpdateHorizontalExtent() { if (!multiColumn && horizontalScrollbar && IsHandleCreated) { int width = horizontalExtent; if (width == 0) { width = MaxItemWidth; } SendMessage(NativeMethods.LB_SETHORIZONTALEXTENT, width, 0); } } // Updates the cached max item width // private void UpdateMaxItemWidth(object item, bool removing) { // We shouldn't be caching maxWidth if we don't have horizontal scrollbars, // or horizontal extent has been set // if (!horizontalScrollbar || horizontalExtent > 0) { maxWidth = -1; return; } // Only update if we are currently caching maxWidth // if (maxWidth > -1) { // Compute item width // int width; using (Graphics graphics = CreateGraphicsInternal()) { width = (int)(Math.Ceiling(graphics.MeasureString(GetItemText(item), this.Font).Width)); } if (removing) { // We're removing this item, so if it's the longest // in the list, reset the cache // if (width >= maxWidth) { maxWidth = -1; } } else { // We're adding or inserting this item - update the cache // if (width > maxWidth) { maxWidth = width; } } } } // Updates the Custom TabOffsets // private void UpdateCustomTabOffsets() { if (IsHandleCreated && UseCustomTabOffsets && CustomTabOffsets != null) { int wpar = CustomTabOffsets.Count; int[] offsets = new int[wpar]; CustomTabOffsets.CopyTo(offsets, 0); UnsafeNativeMethods.SendMessage(new HandleRef(this, Handle), NativeMethods.LB_SETTABSTOPS, wpar, offsets); Invalidate(); } } private void WmPrint(ref Message m) { base.WndProc(ref m); if ((NativeMethods.PRF_NONCLIENT & (int)m.LParam) != 0 && Application.RenderWithVisualStyles && this.BorderStyle == BorderStyle.Fixed3D) { IntSecurity.UnmanagedCode.Assert(); try { using (Graphics g = Graphics.FromHdc(m.WParam)) { Rectangle rect = new Rectangle(0, 0, this.Size.Width - 1, this.Size.Height - 1); g.DrawRectangle(new Pen(VisualStyleInformation.TextControlBorder), rect); rect.Inflate(-1, -1); g.DrawRectangle(SystemPens.Window, rect); } } finally { CodeAccessPermission.RevertAssert(); } } } /// /// /// ///[ System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.InheritanceDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode), System.Security.Permissions.SecurityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand, Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode) ] protected virtual void WmReflectCommand(ref Message m) { switch (NativeMethods.Util.HIWORD(m.WParam)) { case NativeMethods.LBN_SELCHANGE: if (selectedItems != null) { selectedItems.Dirty(); } OnSelectedIndexChanged(EventArgs.Empty); break; case NativeMethods.LBN_DBLCLK: // Handle this inside WM_LBUTTONDBLCLK // OnDoubleClick(EventArgs.Empty); break; } } /// /// /// ///private void WmReflectDrawItem(ref Message m) { NativeMethods.DRAWITEMSTRUCT dis = (NativeMethods.DRAWITEMSTRUCT)m.GetLParam(typeof(NativeMethods.DRAWITEMSTRUCT)); IntPtr dc = dis.hDC; IntPtr oldPal = SetUpPalette(dc, false /*force*/, false /*realize*/); try { Graphics g = Graphics.FromHdcInternal(dc); try { Rectangle bounds = Rectangle.FromLTRB(dis.rcItem.left, dis.rcItem.top, dis.rcItem.right, dis.rcItem.bottom); if (HorizontalScrollbar) { if (MultiColumn) { bounds.Width = Math.Max(ColumnWidth, bounds.Width); } else { bounds.Width = Math.Max(MaxItemWidth, bounds.Width); } } OnDrawItem(new DrawItemEventArgs(g, Font, bounds, dis.itemID, (DrawItemState)dis.itemState, ForeColor, BackColor)); } finally { g.Dispose(); } } finally { if (oldPal != IntPtr.Zero) { SafeNativeMethods.SelectPalette(new HandleRef(null, dc), new HandleRef(null, oldPal), 0); } } m.Result = (IntPtr)1; } /// /// /// ///// This method is only called if in owner draw mode private void WmReflectMeasureItem(ref Message m) { NativeMethods.MEASUREITEMSTRUCT mis = (NativeMethods.MEASUREITEMSTRUCT)m.GetLParam(typeof(NativeMethods.MEASUREITEMSTRUCT)); if (drawMode == DrawMode.OwnerDrawVariable && mis.itemID >= 0) { Graphics graphics = CreateGraphicsInternal(); MeasureItemEventArgs mie = new MeasureItemEventArgs(graphics, mis.itemID, ItemHeight); try { OnMeasureItem(mie); mis.itemHeight = mie.ItemHeight; } finally { graphics.Dispose(); } } else { mis.itemHeight = ItemHeight; } Marshal.StructureToPtr(mis, m.LParam, false); m.Result = (IntPtr)1; } /// /// /// The list's window procedure. Inheriting classes can override this /// to add extra functionality, but should not forget to call /// base.wndProc(m); to ensure the list continues to function properly. /// [SecurityPermission(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.UnmanagedCode)] protected override void WndProc(ref Message m) { switch (m.Msg) { case NativeMethods.WM_REFLECT + NativeMethods.WM_COMMAND: WmReflectCommand(ref m); break; case NativeMethods.WM_REFLECT + NativeMethods.WM_DRAWITEM: WmReflectDrawItem(ref m); break; case NativeMethods.WM_REFLECT + NativeMethods.WM_MEASUREITEM: WmReflectMeasureItem(ref m); break; case NativeMethods.WM_PRINT: WmPrint(ref m); break; case NativeMethods.WM_LBUTTONDOWN: if (selectedItems != null) { selectedItems.Dirty(); } base.WndProc(ref m); break; case NativeMethods.WM_LBUTTONUP: // Get the mouse location // int x = NativeMethods.Util.SignedLOWORD(m.LParam); int y = NativeMethods.Util.SignedHIWORD(m.LParam); Point pt = new Point(x,y); pt = PointToScreen(pt); bool captured = Capture; if (captured && UnsafeNativeMethods.WindowFromPoint(pt.X, pt.Y) == Handle) { if (!doubleClickFired && !ValidationCancelled) { OnClick(new MouseEventArgs(MouseButtons.Left, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); OnMouseClick(new MouseEventArgs(MouseButtons.Left, 1, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); } else { doubleClickFired = false; // WM_COMMAND is only fired if the user double clicks an item, // so we can't use that as a double-click substitute if (!ValidationCancelled) { OnDoubleClick(new MouseEventArgs(MouseButtons.Left, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); OnMouseDoubleClick(new MouseEventArgs(MouseButtons.Left, 2, NativeMethods.Util.SignedLOWORD(m.LParam), NativeMethods.Util.SignedHIWORD(m.LParam), 0)); } } } // // If this control has been disposed in the user's event handler, then we need to ignore the WM_LBUTTONUP // message to avoid exceptions thrown as a result of handle re-creation (VSWhidbey#95150). // We handle this situation here and not at the top of the window procedure since this is the only place // where we can get disposed as an effect of external code (form.Close() for instance) and then pass the // message to the base class. // if (GetState(STATE_DISPOSED)) { base.DefWndProc(ref m); } else { base.WndProc(ref m); } doubleClickFired = false; break; case NativeMethods.WM_RBUTTONUP: // Get the mouse location // int rx = NativeMethods.Util.SignedLOWORD(m.LParam); int ry = NativeMethods.Util.SignedHIWORD(m.LParam); Point rpt = new Point(rx,ry); rpt = PointToScreen(rpt); bool rCaptured = Capture; if (rCaptured && UnsafeNativeMethods.WindowFromPoint(rpt.X, rpt.Y) == Handle) { if (selectedItems != null) { selectedItems.Dirty(); } } base.WndProc(ref m); break; case NativeMethods.WM_LBUTTONDBLCLK: //the Listbox gets WM_LBUTTONDOWN - WM_LBUTTONUP -WM_LBUTTONDBLCLK - WM_LBUTTONUP... //sequence for doubleclick... //the first WM_LBUTTONUP, resets the flag for Doubleclick //So its necessary for us to set it again... doubleClickFired = true; base.WndProc(ref m); break; case NativeMethods.WM_WINDOWPOSCHANGED: base.WndProc(ref m); if (integralHeight && fontIsChanged) { Height = Math.Max(Height,ItemHeight); fontIsChanged = false; } break; default: base.WndProc(ref m); break; } } ////// This is similar to ArrayList except that it also /// mantains a bit-flag based state element for each item /// in the array. /// /// The methods to enumerate, count and get data support /// virtualized indexes. Indexes are virtualized according /// to the state mask passed in. This allows ItemArray /// to be the backing store for one read-write "master" /// collection and serveral read-only collections based /// on masks. ItemArray supports up to 31 masks. /// internal class ItemArray : IComparer { private static int lastMask = 1; private ListControl listControl; private Entry[] entries; private int count; private int version; public ItemArray(ListControl listControl) { this.listControl = listControl; } ////// The version of this array. This number changes with each /// change to the item list. /// public int Version { get { return version; } } ////// Adds the given item to the array. The state is initially /// zero. /// public object Add(object item) { EnsureSpace(1); version++; entries[count] = new Entry(item); return entries[count++]; } ////// Adds the given collection of items to the array. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public void AddRange(ICollection items) { if (items == null) { throw new ArgumentNullException("items"); } EnsureSpace(items.Count); foreach(object i in items) { entries[count++] = new Entry(i); } version++; } ////// Clears this array. /// public void Clear() { count = 0; version++; } ////// Allocates a new bitmask for use. /// public static int CreateMask() { int mask = lastMask; lastMask = lastMask << 1; Debug.Assert(lastMask > mask, "We have overflowed our state mask."); return mask; } ////// Ensures that our internal array has space for /// the requested # of elements. /// private void EnsureSpace(int elements) { if (entries == null) { entries = new Entry[Math.Max(elements, 4)]; } else if (count + elements >= entries.Length) { int newLength = Math.Max(entries.Length * 2, entries.Length + elements); Entry[] newEntries = new Entry[newLength]; entries.CopyTo(newEntries, 0); entries = newEntries; } } ////// Turns a virtual index into an actual index. /// public int GetActualIndex(int virtualIndex, int stateMask) { if (stateMask == 0) { return virtualIndex; } // More complex; we must compute this index. int calcIndex = -1; for(int i = 0; i < count; i++) { if ((entries[i].state & stateMask) != 0) { calcIndex++; if (calcIndex == virtualIndex) { return i; } } } return -1; } ////// Gets the count of items matching the given mask. /// public int GetCount(int stateMask) { // If mask is zero, then just give the main count if (stateMask == 0) { return count; } // more complex: must provide a count of items // based on a mask. int filteredCount = 0; for(int i = 0; i < count; i++) { if ((entries[i].state & stateMask) != 0) { filteredCount++; } } return filteredCount; } ////// Retrieves an enumerator that will enumerate based on /// the given mask. /// public IEnumerator GetEnumerator(int stateMask) { return GetEnumerator(stateMask, false); } ////// Retrieves an enumerator that will enumerate based on /// the given mask. /// public IEnumerator GetEnumerator(int stateMask, bool anyBit) { return new EntryEnumerator(this, stateMask, anyBit); } ////// Gets the item at the given index. The index is /// virtualized against the given mask value. /// public object GetItem(int virtualIndex, int stateMask) { int actualIndex = GetActualIndex(virtualIndex, stateMask); if (actualIndex == -1) { throw new IndexOutOfRangeException(); } return entries[actualIndex].item; } ////// Gets the item at the given index. The index is /// virtualized against the given mask value. /// internal object GetEntryObject(int virtualIndex, int stateMask) { int actualIndex = GetActualIndex(virtualIndex, stateMask); if (actualIndex == -1) { throw new IndexOutOfRangeException(); } return entries[actualIndex]; } ////// Returns true if the requested state mask is set. /// The index is the actual index to the array. /// public bool GetState(int index, int stateMask) { return ((entries[index].state & stateMask) == stateMask); } ////// Returns the virtual index of the item based on the /// state mask. /// public int IndexOf(object item, int stateMask) { int virtualIndex = -1; for(int i = 0; i < count; i++) { if (stateMask == 0 || (entries[i].state & stateMask) != 0) { virtualIndex++; if (entries[i].item.Equals(item)) { return virtualIndex; } } } return -1; } ////// Returns the virtual index of the item based on the /// state mask. Uses reference equality to identify the /// given object in the list. /// public int IndexOfIdentifier(object identifier, int stateMask) { int virtualIndex = -1; for(int i = 0; i < count; i++) { if (stateMask == 0 || (entries[i].state & stateMask) != 0) { virtualIndex++; if (entries[i] == identifier) { return virtualIndex; } } } return -1; } ////// Inserts item at the given index. The index /// is not virtualized. /// public void Insert(int index, object item) { EnsureSpace(1); if (index < count) { System.Array.Copy(entries, index, entries, index + 1, count - index); } entries[index] = new Entry(item); count++; version++; } ////// Removes the given item from the array. If /// the item is not in the array, this does nothing. /// public void Remove(object item) { int index = IndexOf(item, 0); if (index != -1) { RemoveAt(index); } } ////// Removes the item at the given index. /// public void RemoveAt(int index) { count--; for (int i = index; i < count; i++) { entries[i] = entries[i+1]; } entries[count] = null; version++; } ////// Sets the item at the given index to a new value. /// public void SetItem(int index, object item) { entries[index].item = item; } ////// Sets the state data for the given index. /// public void SetState(int index, int stateMask, bool value) { if (value) { entries[index].state |= stateMask; } else { entries[index].state &= ~stateMask; } version++; } ////// Find element in sorted array. If element is not found returns a binary complement of index for inserting /// public int BinarySearch(object element) { return Array.BinarySearch(entries, 0, count, element, this); } ////// Sorts our array. /// public void Sort() { Array.Sort(entries, 0, count, this); } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] public void Sort(Array externalArray) { Array.Sort(externalArray, this); } int IComparer.Compare(object item1, object item2) { if (item1 == null) { if (item2 == null) return 0; //both null, then they are equal return -1; //item1 is null, but item2 is valid (greater) } if (item2 == null) return 1; //item2 is null, so item 1 is greater if (item1 is Entry) { item1 = ((Entry)item1).item; } if (item2 is Entry) { item2 = ((Entry)item2).item; } String itemName1 = listControl.GetItemText(item1); String itemName2 = listControl.GetItemText(item2); CompareInfo compInfo = (Application.CurrentCulture).CompareInfo; return compInfo.Compare(itemName1, itemName2, CompareOptions.StringSort); } ////// This is a single entry in our item array. /// private class Entry { public object item; public int state; public Entry(object item) { this.item = item; this.state = 0; } } ////// EntryEnumerator is an enumerator that will enumerate over /// a given state mask. /// private class EntryEnumerator : IEnumerator { private ItemArray items; private bool anyBit; private int state; private int current; private int version; ////// Creates a new enumerator that will enumerate over the given state. /// public EntryEnumerator(ItemArray items, int state, bool anyBit) { this.items = items; this.state = state; this.anyBit = anyBit; this.version = items.version; this.current = -1; } ////// Moves to the next element, or returns false if at the end. /// bool IEnumerator.MoveNext() { if(version != items.version) throw new InvalidOperationException(SR.GetString(SR.ListEnumVersionMismatch)); while(true) { if (current < items.count - 1) { current++; if (anyBit) { if ((items.entries[current].state & state) != 0) { return true; } } else { if ((items.entries[current].state & state) == state) { return true; } } } else { current = items.count; return false; } } } ////// Resets the enumeration back to the beginning. /// void IEnumerator.Reset() { if(version != items.version) throw new InvalidOperationException(SR.GetString(SR.ListEnumVersionMismatch)); current = -1; } ////// Retrieves the current value in the enumerator. /// object IEnumerator.Current { get { if (current == -1 || current == items.count) { throw new InvalidOperationException(SR.GetString(SR.ListEnumCurrentOutOfRange)); } return items.entries[current].item; } } } } // Items ////// /// [ListBindable(false)] public class ObjectCollection : IList { private ListBox owner; private ItemArray items; ////// A collection that stores objects. /// ////// /// public ObjectCollection(ListBox owner) { this.owner = owner; } ///[To be supplied.] ////// /// public ObjectCollection(ListBox owner, ObjectCollection value) { this.owner = owner; this.AddRange(value); } ////// Initializes a new instance of ListBox.ObjectCollection based on another ListBox.ObjectCollection. /// ////// /// public ObjectCollection(ListBox owner, object[] value) { this.owner = owner; this.AddRange(value); } ////// Initializes a new instance of ListBox.ObjectCollection containing any array of objects. /// ////// /// Retrieves the number of items. /// public int Count { get { return InnerArray.GetCount(0); } } ////// Internal access to the actual data store. /// internal ItemArray InnerArray { get { if (items == null) { items = new ItemArray(owner); } return items; } } ////// object ICollection.SyncRoot { get { return this; } } /// /// bool ICollection.IsSynchronized { get { return false; } } /// /// bool IList.IsFixedSize { get { return false; } } /// /// /// public bool IsReadOnly { get { return false; } } ///[To be supplied.] ////// /// Adds an item to the List box. For an unsorted List box, the item is /// added to the end of the existing list of items. For a sorted List box, /// the item is inserted into the list according to its sorted position. /// The item's toString() method is called to obtain the string that is /// displayed in the combo box. /// A SystemException occurs if there is insufficient space available to /// store the new item. /// public int Add(object item) { owner.CheckNoDataSource(); int index = AddInternal(item); owner.UpdateHorizontalExtent(); return index; } private int AddInternal(object item) { if (item == null) { throw new ArgumentNullException("item"); } int index = -1; if (!owner.sorted) { InnerArray.Add(item); } else { if (Count > 0) { index = InnerArray.BinarySearch(item); if (index < 0) { index = ~index; // getting the index of the first element that is larger than the search value //this index will be used for insert } } else index = 0; Debug.Assert(index >= 0 && index <= Count, "Wrong index for insert"); InnerArray.Insert(index, item); } bool successful = false; try { if (owner.sorted) { if (owner.IsHandleCreated) { owner.NativeInsert(index, item); owner.UpdateMaxItemWidth(item, false); if (owner.selectedItems != null) { // VSWhidbey 95187: sorting may throw the LB contents and the selectedItem array out of synch. owner.selectedItems.Dirty(); } } } else { index = Count - 1; if (owner.IsHandleCreated) { owner.NativeAdd(item); owner.UpdateMaxItemWidth(item, false); } } successful = true; } finally { if (!successful) { InnerArray.Remove(item); } } return index; } ////// int IList.Add(object item) { return Add(item); } /// /// /// public void AddRange(ObjectCollection value) { owner.CheckNoDataSource(); AddRangeInternal((ICollection)value); } ///[To be supplied.] ////// /// public void AddRange(object[] items) { owner.CheckNoDataSource(); AddRangeInternal((ICollection)items); } internal void AddRangeInternal(ICollection items) { if (items == null) { throw new ArgumentNullException("items"); } owner.BeginUpdate(); try { foreach (object item in items) { // adding items one-by-one for performance // not using sort because after the array is sorted index of each newly added item will need to be found // AddInternal is based on BinarySearch and finds index without any additional cost AddInternal(item); } } finally { owner.UpdateHorizontalExtent(); owner.EndUpdate(); } } ///[To be supplied.] ////// /// Retrieves the item with the specified index. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public virtual object this[int index] { get { if (index < 0 || index >= InnerArray.GetCount(0)) { throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); } return InnerArray.GetItem(index, 0); } set { owner.CheckNoDataSource(); SetItemInternal(index, value); } } ////// /// Removes all items from the ListBox. /// public virtual void Clear() { owner.CheckNoDataSource(); ClearInternal(); } ////// Removes all items from the ListBox. Bypasses the data source check. /// internal void ClearInternal() { //update the width.. to reset Scrollbars.. // Clear the selection state. // int cnt = owner.Items.Count; for (int i = 0; i < cnt; i++) { owner.UpdateMaxItemWidth(InnerArray.GetItem(i, 0), true); } if (owner.IsHandleCreated) { owner.NativeClear(); } InnerArray.Clear(); owner.maxWidth = -1; owner.UpdateHorizontalExtent(); } ////// /// public bool Contains(object value) { return IndexOf(value) != -1; } ///[To be supplied.] ////// /// Copies the ListBox Items collection to a destination array. /// public void CopyTo(object[] destination, int arrayIndex) { int count = InnerArray.GetCount(0); for(int i = 0; i < count; i++) { destination[i + arrayIndex] = InnerArray.GetItem(i, 0); } } ////// void ICollection.CopyTo(Array destination, int index) { int count = InnerArray.GetCount(0); for(int i = 0; i < count; i++) { destination.SetValue(InnerArray.GetItem(i, 0), i + index); } } /// /// /// Returns an enumerator for the ListBox Items collection. /// public IEnumerator GetEnumerator() { return InnerArray.GetEnumerator(0); } ////// /// public int IndexOf(object value) { if (value == null) { throw new ArgumentNullException("value"); } return InnerArray.IndexOf(value,0); } ///[To be supplied.] ////// /// ///[To be supplied.] ///internal int IndexOfIdentifier(object value) { if (value == null) { throw new ArgumentNullException("value"); } return InnerArray.IndexOfIdentifier(value,0); } /// /// /// Adds an item to the combo box. For an unsorted combo box, the item is /// added to the end of the existing list of items. For a sorted combo box, /// the item is inserted into the list according to its sorted position. /// The item's toString() method is called to obtain the string that is /// displayed in the combo box. /// A SystemException occurs if there is insufficient space available to /// store the new item. /// public void Insert(int index, object item) { owner.CheckNoDataSource(); if (index < 0 || index > InnerArray.GetCount(0)) { throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); } // If the combo box is sorted, then nust treat this like an add // because we are going to twiddle the index anyway. // if (owner.sorted) { Add(item); } else { InnerArray.Insert(index, item); if (owner.IsHandleCreated) { bool successful = false; try { owner.NativeInsert(index, item); owner.UpdateMaxItemWidth(item, false); successful = true; } finally { if (!successful) { InnerArray.RemoveAt(index); } } } } owner.UpdateHorizontalExtent(); } ////// /// Removes the given item from the ListBox, provided that it is /// actually in the list. /// public void Remove(object value) { int index = InnerArray.IndexOf(value, 0); if (index != -1) { RemoveAt(index); } } ////// /// Removes an item from the ListBox at the given index. /// public void RemoveAt(int index) { owner.CheckNoDataSource(); if (index < 0 || index >= InnerArray.GetCount(0)) { throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); } owner.UpdateMaxItemWidth(InnerArray.GetItem(index, 0), true); // VSWhidbey 95181: Update InnerArray before calling NativeRemoveAt to ensure that when // SelectedIndexChanged is raised (by NativeRemoveAt), InnerArray's state matches wrapped LB state. InnerArray.RemoveAt(index); if (owner.IsHandleCreated) { owner.NativeRemoveAt(index); } owner.UpdateHorizontalExtent(); } internal void SetItemInternal(int index, object value) { if (value == null) { throw new ArgumentNullException("value"); } if (index < 0 || index >= InnerArray.GetCount(0)) { throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); } owner.UpdateMaxItemWidth(InnerArray.GetItem(index, 0), true); InnerArray.SetItem(index, value); // If the native control has been created, and the display text of the new list item object // is different to the current text in the native list item, recreate the native list item... if (owner.IsHandleCreated) { bool selected = (owner.SelectedIndex == index); if (String.Compare(this.owner.GetItemText(value), this.owner.NativeGetItemText(index), true, CultureInfo.CurrentCulture) != 0) { owner.NativeRemoveAt(index); owner.SelectedItems.SetSelected(index, false); owner.NativeInsert(index, value); owner.UpdateMaxItemWidth(value, false); if (selected) { owner.SelectedIndex = index; } } else { // NEW - FOR COMPATIBILITY REASONS // Minimum compatibility fix for VSWhidbey 377287 if (selected) { owner.OnSelectedIndexChanged(EventArgs.Empty); //will fire selectedvaluechanged } } } owner.UpdateHorizontalExtent(); } } // end ObjectCollection //***************************************************************************************** // IntegerCollection ////// /// public class IntegerCollection : IList { private ListBox owner; private int[] innerArray; private int count=0; ///[To be supplied.] ////// /// public IntegerCollection(ListBox owner) { this.owner = owner; } ///[To be supplied.] ////// /// [Browsable(false)] public int Count { get { return count; } } ///Number of current selected items. ////// object ICollection.SyncRoot { get { return this; } } /// /// bool ICollection.IsSynchronized { get { return true; } } /// /// bool IList.IsFixedSize { get { return false; } } /// /// /// bool IList.IsReadOnly { get { return false; } } ///[To be supplied.] ////// /// public bool Contains(int item) { return IndexOf(item) != -1; } ///[To be supplied.] ////// bool IList.Contains(object item) { if (item is Int32) { return Contains((int)item); } else { return false; } } public void Clear() { count = 0; innerArray = null; } /// /// /// public int IndexOf(int item) { return Array.IndexOf(innerArray, item); } ///[To be supplied.] ////// int IList.IndexOf(object item) { if (item is Int32) { return IndexOf((int)item); } else { return -1; } } /// /// Add a unique integer to the collection in sorted order. /// A SystemException occurs if there is insufficient space available to /// store the new item. /// private int AddInternal(int item) { EnsureSpace(1); int index = IndexOf(item); if (index == -1) { innerArray[count++] = item; Array.Sort(innerArray,0,count); index = IndexOf(item); } return index; } ////// /// Adds a unique integer to the collection in sorted order. /// A SystemException occurs if there is insufficient space available to /// store the new item. /// public int Add(int item) { int index = AddInternal(item); owner.UpdateCustomTabOffsets(); return index; } ////// [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] [ SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "item" is the name of the param passed in. // So we don't have to localize it. ] int IList.Add(object item) { if (!(item is int)) { throw new ArgumentException("item"); } return Add((int)item); } /// /// /// public void AddRange(int[] items) { AddRangeInternal((ICollection)items); } ///[To be supplied.] ////// /// public void AddRange(IntegerCollection value) { AddRangeInternal((ICollection)value); } ///[To be supplied.] ////// Add range that bypasses the data source check. /// [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] [ SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "item" is the name of the param passed in. // So we don't have to localize it. ] private void AddRangeInternal(ICollection items) { if (items == null) { throw new ArgumentNullException("items"); } owner.BeginUpdate(); try { EnsureSpace(items.Count); foreach(object item in items) { if (!(item is int)) { throw new ArgumentException("item"); } else { AddInternal((int)item); } } owner.UpdateCustomTabOffsets(); } finally { owner.EndUpdate(); } } ////// Ensures that our internal array has space for /// the requested # of elements. /// private void EnsureSpace(int elements) { if (innerArray == null) { innerArray = new int[Math.Max(elements, 4)]; } else if (count + elements >= innerArray.Length) { int newLength = Math.Max(innerArray.Length * 2, innerArray.Length + elements); int[] newEntries = new int[newLength]; innerArray.CopyTo(newEntries, 0); innerArray = newEntries; } } ////// void IList.Clear() { Clear(); } /// /// void IList.Insert(int index, object value) { throw new NotSupportedException(SR.GetString(SR.ListBoxCantInsertIntoIntegerCollection)); } /// /// [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] [ SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "value" is the name of the param passed in. // So we don't have to localize it. ] void IList.Remove(object value) { if (!(value is int)) { throw new ArgumentException("value"); } Remove((int)value); } /// /// void IList.RemoveAt(int index) { RemoveAt(index); } /// /// /// Removes the given item from the array. If /// the item is not in the array, this does nothing. /// public void Remove(int item) { int index = IndexOf(item); if (index != -1) { RemoveAt(index); } } ////// /// Removes the item at the given index. /// public void RemoveAt(int index) { count--; for (int i = index; i < count; i++) { innerArray[i] = innerArray[i+1]; } } ////// /// Retrieves the specified selected item. /// [ SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters") // "index" is the name of the param passed in. // So we don't have to localize it. ] public int this[int index] { get { return innerArray[index]; } [ SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly") // This exception already shipped. // We can't change its text. ] set { if (index < 0 || index >= count) { throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", (index).ToString(CultureInfo.CurrentCulture))); } innerArray[index] = (int)value; owner.UpdateCustomTabOffsets(); } } ////// [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] object IList.this[int index] { get { return this[index]; } [ SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters"), // "value" is the name of the param. // So we don't have to localize it. SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly") // This exception already shipped. // We can't change its text. ] set { if (!(value is int)) { throw new ArgumentException("value"); } else { this[index] = (int)value; } } } /// /// /// public void CopyTo(Array destination, int index) { int cnt = Count; for (int i = 0; i < cnt; i++) { destination.SetValue(this[i], i + index); } } ///[To be supplied.] ////// /// IEnumerator IEnumerable.GetEnumerator() { return new CustomTabOffsetsEnumerator(this); } ///[To be supplied.] ////// EntryEnumerator is an enumerator that will enumerate over /// a given state mask. /// private class CustomTabOffsetsEnumerator : IEnumerator { private IntegerCollection items; private int current; ////// Creates a new enumerator that will enumerate over the given state. /// public CustomTabOffsetsEnumerator(IntegerCollection items) { this.items = items; this.current = -1; } ////// Moves to the next element, or returns false if at the end. /// bool IEnumerator.MoveNext() { if (current < items.Count - 1) { current++; return true; } else { current = items.Count; return false; } } ////// Resets the enumeration back to the beginning. /// void IEnumerator.Reset() { current = -1; } ////// Retrieves the current value in the enumerator. /// object IEnumerator.Current { get { if (current == -1 || current == items.Count) { throw new InvalidOperationException(SR.GetString(SR.ListEnumCurrentOutOfRange)); } return items[current]; } } } } //***************************************************************************************** // SelectedIndices ////// /// public class SelectedIndexCollection : IList { private ListBox owner; /* C#r: protected */ ///[To be supplied.] ////// /// public SelectedIndexCollection(ListBox owner) { this.owner = owner; } ///[To be supplied.] ////// /// [Browsable(false)] public int Count { get { return owner.SelectedItems.Count; } } ///Number of current selected items. ////// object ICollection.SyncRoot { get { return this; } } /// /// bool ICollection.IsSynchronized { get { return true; } } /// /// bool IList.IsFixedSize { get { return true; } } /// /// /// public bool IsReadOnly { get { return true; } } ///[To be supplied.] ////// /// public bool Contains(int selectedIndex) { return IndexOf(selectedIndex) != -1; } ///[To be supplied.] ////// bool IList.Contains(object selectedIndex) { if (selectedIndex is Int32) { return Contains((int)selectedIndex); } else { return false; } } /// /// /// public int IndexOf(int selectedIndex) { // Just what does this do? The selectedIndex parameter above is the index into the // main object collection. We look at the state of that item, and if the state indicates // that it is selected, we get back the virtualized index into this collection. Indexes on // this collection match those on the SelectedObjectCollection. if (selectedIndex >= 0 && selectedIndex < InnerArray.GetCount(0) && InnerArray.GetState(selectedIndex, SelectedObjectCollection.SelectedObjectMask)) { return InnerArray.IndexOf(InnerArray.GetItem(selectedIndex, 0), SelectedObjectCollection.SelectedObjectMask); } return -1; } ///[To be supplied.] ////// int IList.IndexOf(object selectedIndex) { if (selectedIndex is Int32) { return IndexOf((int)selectedIndex); } else { return -1; } } /// /// int IList.Add(object value) { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); } /// /// void IList.Clear() { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); } /// /// void IList.Insert(int index, object value) { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); } /// /// void IList.Remove(object value) { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); } /// /// void IList.RemoveAt(int index) { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); } /// /// /// Retrieves the specified selected item. /// public int this[int index] { get { object identifier = InnerArray.GetEntryObject(index, SelectedObjectCollection.SelectedObjectMask); return InnerArray.IndexOfIdentifier(identifier, 0); } } ////// object IList.this[int index] { get { return this[index]; } set { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedIndexCollectionIsReadOnly)); } } /// /// This is the item array that stores our data. We share this backing store /// with the main object collection. /// private ItemArray InnerArray { get { owner.SelectedItems.EnsureUpToDate(); return ((ObjectCollection)owner.Items).InnerArray; } } ////// /// public void CopyTo(Array destination, int index) { int cnt = Count; for (int i = 0; i < cnt; i++) { destination.SetValue(this[i], i + index); } } ///[To be supplied.] ////// /// public void Clear() { if (owner != null) { owner.ClearSelected(); } } ///[To be supplied.] ////// /// public void Add(int index) { if (owner != null) { ObjectCollection items = owner.Items; if (items != null) { if (index != -1 && !Contains(index)) { owner.SetSelected(index, true); } } } } ///[To be supplied.] ////// /// public void Remove(int index) { if (owner != null) { ObjectCollection items = owner.Items; if (items != null) { if (index != -1 && Contains(index)) { owner.SetSelected(index, false); } } } } ///[To be supplied.] ////// /// public IEnumerator GetEnumerator() { return new SelectedIndexEnumerator(this); } ///[To be supplied.] ////// EntryEnumerator is an enumerator that will enumerate over /// a given state mask. /// private class SelectedIndexEnumerator : IEnumerator { private SelectedIndexCollection items; private int current; ////// Creates a new enumerator that will enumerate over the given state. /// public SelectedIndexEnumerator(SelectedIndexCollection items) { this.items = items; this.current = -1; } ////// Moves to the next element, or returns false if at the end. /// bool IEnumerator.MoveNext() { if (current < items.Count - 1) { current++; return true; } else { current = items.Count; return false; } } ////// Resets the enumeration back to the beginning. /// void IEnumerator.Reset() { current = -1; } ////// Retrieves the current value in the enumerator. /// object IEnumerator.Current { get { if (current == -1 || current == items.Count) { throw new InvalidOperationException(SR.GetString(SR.ListEnumCurrentOutOfRange)); } return items[current]; } } } } // Should be "ObjectCollection", except we already have one of those. ////// /// public class SelectedObjectCollection : IList { // This is the bitmask used within ItemArray to identify selected objects. internal static int SelectedObjectMask = ItemArray.CreateMask(); private ListBox owner; private bool stateDirty; private int lastVersion; private int count; /* C#r: protected */ ///[To be supplied.] ////// /// public SelectedObjectCollection(ListBox owner) { this.owner = owner; this.stateDirty = true; this.lastVersion = -1; } ///[To be supplied.] ////// /// Number of current selected items. /// public int Count { get { if (owner.IsHandleCreated) { SelectionMode current = (owner.selectionModeChanging) ? owner.cachedSelectionMode : owner.selectionMode; switch (current) { case SelectionMode.None: return 0; case SelectionMode.One: int index = owner.SelectedIndex; if (index >= 0) { return 1; } return 0; case SelectionMode.MultiSimple: case SelectionMode.MultiExtended: return (int)owner.SendMessage(NativeMethods.LB_GETSELCOUNT, 0, 0); } return 0; } // If the handle hasn't been created, we must do this the hard way. // Getting the count when using a mask is expensive, so cache it. // if (lastVersion != InnerArray.Version) { lastVersion = InnerArray.Version; count = InnerArray.GetCount(SelectedObjectMask); } return count; } } ////// object ICollection.SyncRoot { get { return this; } } /// /// bool ICollection.IsSynchronized { get { return false; } } /// /// bool IList.IsFixedSize { get { return true; } } /// /// Called by the list box to dirty the selected item state. /// internal void Dirty() { stateDirty = true; } ////// This is the item array that stores our data. We share this backing store /// with the main object collection. /// private ItemArray InnerArray { get { EnsureUpToDate(); return ((ObjectCollection)owner.Items).InnerArray; } } ////// This is the function that Ensures that the selections are uptodate with /// current listbox handle selections. /// internal void EnsureUpToDate() { if (stateDirty) { stateDirty = false; if (owner.IsHandleCreated) { owner.NativeUpdateSelection(); } } } ////// /// public bool IsReadOnly { get { return true; } } ///[To be supplied.] ////// /// public bool Contains(object selectedObject) { return IndexOf(selectedObject) != -1; } ///[To be supplied.] ////// /// public int IndexOf(object selectedObject) { return InnerArray.IndexOf(selectedObject, SelectedObjectMask); } ///[To be supplied.] ////// int IList.Add(object value) { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); } /// /// void IList.Clear() { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); } /// /// void IList.Insert(int index, object value) { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); } /// /// void IList.Remove(object value) { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); } /// /// void IList.RemoveAt(int index) { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); } // A new internal method used in SelectedIndex getter... // For a Multi select ListBox there can be two items with the same name ... // and hence a object comparison is required... // This method returns the "object" at the passed index rather than the "item" ... // this "object" is then compared in the IndexOf( ) method of the itemsCollection. // /// /// internal object GetObjectAt(int index) { return InnerArray.GetEntryObject(index, SelectedObjectCollection.SelectedObjectMask); } /// /// /// Retrieves the specified selected item. /// [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] public object this[int index] { get { return InnerArray.GetItem(index, SelectedObjectMask); } set { throw new NotSupportedException(SR.GetString(SR.ListBoxSelectedObjectCollectionIsReadOnly)); } } ////// /// public void CopyTo(Array destination, int index) { int cnt = InnerArray.GetCount(SelectedObjectMask); for (int i = 0; i < cnt; i++) { destination.SetValue(InnerArray.GetItem(i, SelectedObjectMask), i + index); } } ///[To be supplied.] ////// /// public IEnumerator GetEnumerator() { return InnerArray.GetEnumerator(SelectedObjectMask); } ///[To be supplied.] ////// This method returns if the actual item index is selected. The index is the index to the MAIN /// collection, not this one. /// internal bool GetSelected(int index) { return InnerArray.GetState(index, SelectedObjectMask); } // when SelectedObjectsCollection::ItemArray is accessed we push the selection from Native ListBox into our .Net ListBox - see EnsureUpToDate() // when we create the handle we need to be able to do the opposite : push the selection from .Net ListBox into Native ListBox internal void PushSelectionIntoNativeListBox(int index) { // we can't use ItemArray accessor because this will wipe out our Selection collection bool selected = ((ObjectCollection)owner.Items).InnerArray.GetState(index, SelectedObjectMask); // push selection only if the item is actually selected // this also takes care of the case where owner.SelectionMode == SelectionMode.One if (selected) { this.owner.NativeSetSelected(index, true /*we signal selection to the native listBox only if the item is actually selected*/); } } ////// Same thing for GetSelected. /// internal void SetSelected(int index, bool value) { InnerArray.SetState(index, SelectedObjectMask, value); } ////// /// public void Clear() { if (owner != null) { owner.ClearSelected(); } } ///[To be supplied.] ////// /// public void Add(object value) { if (owner != null) { ObjectCollection items = owner.Items; if (items != null && value != null) { int index = items.IndexOf(value); if (index != -1 && !GetSelected(index)) { owner.SelectedIndex = index; } } } } ///[To be supplied.] ////// /// public void Remove(object value) { if (owner != null) { ObjectCollection items = owner.Items; if (items != null & value != null) { int index = items.IndexOf(value); if (index != -1 && GetSelected(index)) { owner.SetSelected(index, false); } } } } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007.[To be supplied.] ///
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- DataGridViewCellFormattingEventArgs.cs
- SqlWorkflowInstanceStore.cs
- AdditionalEntityFunctions.cs
- loginstatus.cs
- ExpressionBuilder.cs
- AssemblyCache.cs
- SoapMessage.cs
- _UriSyntax.cs
- OracleParameter.cs
- ChildrenQuery.cs
- PieceDirectory.cs
- DocumentViewerBase.cs
- DataTableMapping.cs
- PtsHost.cs
- AttributeConverter.cs
- SecurityUtils.cs
- WebUtil.cs
- Size3DValueSerializer.cs
- StsCommunicationException.cs
- DrawingContextWalker.cs
- TaskSchedulerException.cs
- CustomErrorCollection.cs
- AssertSection.cs
- TitleStyle.cs
- CompositionTarget.cs
- XmlCollation.cs
- ModuleConfigurationInfo.cs
- ParsedRoute.cs
- QualifiedCellIdBoolean.cs
- ControlUtil.cs
- Internal.cs
- ProcessHostMapPath.cs
- ObjectTag.cs
- ControlPaint.cs
- WebServiceParameterData.cs
- ModelTreeManager.cs
- AjaxFrameworkAssemblyAttribute.cs
- ApplicationHost.cs
- ObjectDataSourceEventArgs.cs
- PropertySourceInfo.cs
- PersistenceTypeAttribute.cs
- AppDomainUnloadedException.cs
- SafeRegistryHandle.cs
- Membership.cs
- PenContext.cs
- TableRow.cs
- MiniMapControl.xaml.cs
- NotifyParentPropertyAttribute.cs
- TraceLevelStore.cs
- ScrollableControl.cs
- Typography.cs
- Misc.cs
- XmlSchemaGroupRef.cs
- CaretElement.cs
- TextRunProperties.cs
- PageThemeParser.cs
- SchemaConstraints.cs
- XmlTextEncoder.cs
- SubstitutionList.cs
- EditBehavior.cs
- EntityViewGenerationConstants.cs
- ClipboardProcessor.cs
- MessageContractExporter.cs
- TrustLevelCollection.cs
- Condition.cs
- ExpressionBinding.cs
- PathFigureCollection.cs
- FontFaceLayoutInfo.cs
- DesignerDataStoredProcedure.cs
- ReferencedCategoriesDocument.cs
- HttpRuntime.cs
- TransformProviderWrapper.cs
- GatewayDefinition.cs
- exports.cs
- UniformGrid.cs
- SpellerHighlightLayer.cs
- WindowsScrollBar.cs
- WindowsBrush.cs
- PtsHost.cs
- PerformanceCounterCategory.cs
- DataService.cs
- HtmlMeta.cs
- XmlNamedNodeMap.cs
- ObjectListDataBindEventArgs.cs
- MatrixValueSerializer.cs
- SymmetricCryptoHandle.cs
- PolicyAssertionCollection.cs
- SqlDataSourceFilteringEventArgs.cs
- ReadWriteObjectLock.cs
- NotifyCollectionChangedEventArgs.cs
- DbMetaDataCollectionNames.cs
- TypeConverter.cs
- EdmItemError.cs
- ActivityWithResult.cs
- SoundPlayer.cs
- ListViewTableRow.cs
- Wow64ConfigurationLoader.cs
- Pair.cs
- LayoutSettings.cs
- ObjectDataSourceDisposingEventArgs.cs