// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; using System.ComponentModel; namespace System.Windows.Forms; public partial class DataGridViewComboBoxCell : DataGridViewCell { /// <summary> /// A collection that stores objects. /// </summary> [ListBindable(false)] public class ObjectCollection : IList { private readonly DataGridViewComboBoxCell _owner; private List<object>? _items; private IComparer<object>? _comparer; public ObjectCollection(DataGridViewComboBoxCell owner) { Debug.Assert(owner is not null); _owner = owner; } private IComparer<object> Comparer => _comparer ??= new ItemComparer(_owner); /// <summary> /// Retrieves the number of items. /// </summary> public int Count => InnerArray.Count; /// <summary> /// Internal access to the actual data store. /// </summary> internal List<object> InnerArray => _items ??= []; object ICollection.SyncRoot => ((ICollection)InnerArray).SyncRoot; bool ICollection.IsSynchronized => ((ICollection)InnerArray).IsSynchronized; bool IList.IsFixedSize => ((IList)InnerArray).IsFixedSize; public bool IsReadOnly => ((IList)InnerArray).IsReadOnly; /// <summary> /// Adds an item to the collection. 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. /// </summary> public int Add(object item) { _owner.CheckNoDataSource(); ArgumentNullException.ThrowIfNull(item); int index = ((IList)InnerArray).Add(item); bool success = false; if (_owner.Sorted) { try { InnerArray.Sort(Comparer); index = InnerArray.IndexOf(item); success = true; } finally { if (!success) { InnerArray.Remove(item); } } } _owner.OnItemsCollectionChanged(); return index; } int IList.Add(object? item) => Add(item!); public void AddRange(params object[] items) { _owner.CheckNoDataSource(); AddRangeInternal(items); _owner.OnItemsCollectionChanged(); } public void AddRange(ObjectCollection value) { _owner.CheckNoDataSource(); InnerAddRange(value); _owner.OnItemsCollectionChanged(); void InnerAddRange(ObjectCollection items) { ArgumentNullException.ThrowIfNull(items); foreach (object item in items) { if (item is null) { throw new InvalidOperationException(SR.InvalidNullItemInCollection); } InnerArray.Add(item); } if (_owner.Sorted) { InnerArray.Sort(Comparer); } } } /// <summary> /// Add range that bypasses the data source check. /// </summary> internal void AddRangeInternal(ICollection<object> items) { ArgumentNullException.ThrowIfNull(items); foreach (object item in items) { if (item is null) { throw new InvalidOperationException(SR.InvalidNullItemInCollection); } } // Add everything to the collection first, then sort InnerArray.AddRange(items); if (_owner.Sorted) { InnerArray.Sort(Comparer); } } internal void SortInternal() => InnerArray.Sort(Comparer); /// <summary> /// Retrieves the item with the specified index. /// </summary> public virtual object? this[int index] { get { ArgumentOutOfRangeException.ThrowIfNegative(index); ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, InnerArray.Count); return InnerArray[index]; } set { _owner.CheckNoDataSource(); ArgumentOutOfRangeException.ThrowIfNegative(index); ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, InnerArray.Count); InnerArray[index] = value.OrThrowIfNull(); _owner.OnItemsCollectionChanged(); } } /// <summary> /// Removes all items from the collection. /// </summary> public void Clear() { if (InnerArray.Count > 0) { _owner.CheckNoDataSource(); InnerArray.Clear(); _owner.OnItemsCollectionChanged(); } } internal void ClearInternal() => InnerArray.Clear(); public bool Contains(object? value) => IndexOf(value!) != -1; /// <summary> /// Copies the DataGridViewComboBoxCell Items collection to a destination array. /// </summary> public void CopyTo(object[] destination, int arrayIndex) => ((ICollection)InnerArray).CopyTo(destination, arrayIndex); void ICollection.CopyTo(Array destination, int index) => ((ICollection)InnerArray).CopyTo(destination, index); /// <summary> /// Returns an enumerator for the DataGridViewComboBoxCell Items collection. /// </summary> public IEnumerator GetEnumerator() => InnerArray.GetEnumerator(); public int IndexOf(object? value) { ArgumentNullException.ThrowIfNull(value); return InnerArray.IndexOf(value); } /// <summary> /// Adds an item to the collection. 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. /// </summary> public void Insert(int index, object? item) { _owner.CheckNoDataSource(); ArgumentNullException.ThrowIfNull(item); ArgumentOutOfRangeException.ThrowIfNegative(index); ArgumentOutOfRangeException.ThrowIfGreaterThan(index, InnerArray.Count); // If the combo box is sorted, then just treat this like an add // because we are going to twiddle the index anyway. if (_owner.Sorted) { Add(item); } else { InnerArray.Insert(index, item); _owner.OnItemsCollectionChanged(); } } /// <summary> /// Removes the given item from the collection, provided that it is /// actually in the list. /// </summary> public void Remove(object? value) { int index = InnerArray.IndexOf(value!); if (index != -1) { RemoveAt(index); } } /// <summary> /// Removes an item from the collection at the given index. /// </summary> public void RemoveAt(int index) { _owner.CheckNoDataSource(); ArgumentOutOfRangeException.ThrowIfNegative(index); ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, InnerArray.Count); InnerArray.RemoveAt(index); _owner.OnItemsCollectionChanged(); } } } |