// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
namespace System.Windows.Forms
{
public partial class ComboBox
{
public class ObjectCollection : IList
{
private readonly ComboBox owner;
private ArrayList innerList;
private IComparer comparer;
public ObjectCollection(ComboBox owner)
{
this.owner = owner;
}
private IComparer Comparer
{
get
{
if (comparer == null)
{
comparer = new ItemComparer(owner);
}
return comparer;
}
}
private ArrayList InnerList
{
get
{
if (innerList == null)
{
innerList = new ArrayList();
}
return innerList;
}
}
///
/// Retrieves the number of items.
///
public int Count
{
get
{
return InnerList.Count;
}
}
object ICollection.SyncRoot
{
get
{
return this;
}
}
bool ICollection.IsSynchronized
{
get
{
return false;
}
}
bool IList.IsFixedSize
{
get
{
return false;
}
}
public bool IsReadOnly
{
get
{
return false;
}
}
///
/// 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 int Add(object item)
{
int index = AddInternal(item);
return index;
}
private int AddInternal(object item)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
int index = -1;
if (!owner.sorted)
{
InnerList.Add(item);
}
else
{
index = InnerList.BinarySearch(item, Comparer);
if (index < 0)
{
index = ~index; // getting the index of the first element that is larger than the search value
}
InnerList.Insert(index, item);
}
bool successful = false;
try
{
if (owner.sorted)
{
}
else
{
index = InnerList.Count - 1;
}
successful = true;
}
finally
{
if (!successful)
{
InnerList.Remove(item);
}
}
return index;
}
int IList.Add(object item)
{
return Add(item);
}
///
/// Retrieves the item with the specified index.
///
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public virtual object this[int index]
{
get
{
if (index < 0 || index >= InnerList.Count)
{
throw new ArgumentOutOfRangeException();
}
return InnerList[index];
}
set
{
SetItemInternal(index, value);
}
}
public void AddRange(object[] items)
{
try
{
AddRangeInternal(items);
}
finally
{
}
}
internal void AddRangeInternal(IList items)
{
if (items == null)
{
throw new ArgumentNullException(nameof(items));
}
foreach (object item in items)
{
// adding items one-by-one for performance (especially for sorted combobox)
// we can not rely on ArrayList.Sort since its worst case complexity is n*n
// AddInternal is based on BinarySearch and ensures n*log(n) complexity
AddInternal(item);
}
}
///
/// Removes all items from the ComboBox.
///
public void Clear()
{
ClearInternal();
}
internal void ClearInternal()
{
InnerList.Clear();
owner.selectedIndex = -1;
}
public bool Contains(object value)
{
return IndexOf(value) != -1;
}
///
/// Copies the ComboBox Items collection to a destination array.
///
public void CopyTo(object[] destination, int arrayIndex)
{
InnerList.CopyTo(destination, arrayIndex);
}
void ICollection.CopyTo(Array destination, int index)
{
InnerList.CopyTo(destination, index);
}
///
/// Returns an enumerator for the ComboBox Items collection.
///
public IEnumerator GetEnumerator()
{
return InnerList.GetEnumerator();
}
public int IndexOf(object value)
{
if (value == null)
{
throw new ArgumentNullException(nameof(value));
}
return InnerList.IndexOf(value);
}
///
/// 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)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
if (index < 0 || index > InnerList.Count)
{
throw new ArgumentOutOfRangeException();
}
// 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
{
InnerList.Insert(index, item);
}
}
///
/// Removes an item from the ComboBox at the given index.
///
public void RemoveAt(int index)
{
if (index < 0 || index >= InnerList.Count)
{
throw new ArgumentOutOfRangeException();
}
InnerList.RemoveAt(index);
if (index < owner.selectedIndex)
{
owner.selectedIndex--;
}
}
///
/// Removes the given item from the ComboBox, provided that it is
/// actually in the list.
///
public void Remove(object value)
{
int index = InnerList.IndexOf(value);
if (index != -1)
{
RemoveAt(index);
}
}
internal void SetItemInternal(int index, object value)
{
if (index < 0 || index >= InnerList.Count)
{
throw new ArgumentOutOfRangeException();
}
InnerList[index] = value ?? throw new ArgumentNullException(nameof(value));
}
}
private sealed class ItemComparer : IComparer
{
private readonly ComboBox comboBox;
public ItemComparer(ComboBox comboBox)
{
this.comboBox = comboBox;
}
public int 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
}
string itemName1 = comboBox.GetItemText(item1);
string itemName2 = comboBox.GetItemText(item2);
CompareInfo compInfo = CultureInfo.CurrentCulture.CompareInfo;
return compInfo.Compare(itemName1, itemName2, CompareOptions.StringSort);
}
}
}
}