|
// 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.
#pragma warning disable 1634, 1691 // suppressing PreSharp warnings
using MS.Utility;
using MS.Internal;
using System.Collections;
using System.Globalization;
namespace System.Windows.Media.Media3D
{
/// <summary>
/// A collection of Visual3D objects.
/// </summary>
public sealed class Visual3DCollection : IList, IList<Visual3D>
{
//------------------------------------------------------
//
// Constructors
//
//------------------------------------------------------
#region Constructors
internal Visual3DCollection(IVisual3DContainer owner)
{
_owner = owner;
}
#endregion Constructors
//------------------------------------------------------
//
// Public Methods
//
//------------------------------------------------------
#region Public Methods
/// <summary>
/// Adds the value to the collection.
/// </summary>
public void Add(Visual3D value)
{
VerifyAPIForAdd(value);
int addedPosition = InternalCount;
_collection.Add(value);
InvalidateEnumerators();
// NOTE: The collection must be updated before notifying the Visual.
ConnectChild(addedPosition, value);
Debug_ICC();
}
private void ConnectChild(int index, Visual3D value)
{
value.ParentIndex = index;
_owner.AddChild(value);
}
/// <summary>
/// Inserts the value into the list at the specified position
/// </summary>
public void Insert(int index, Visual3D value)
{
VerifyAPIForAdd(value);
InternalInsert(index, value);
}
/// <summary>
/// Removes the value from the collection.
/// </summary>
public bool Remove(Visual3D value)
{
VerifyAPIReadWrite(value);
if (!_collection.Contains(value))
{
return false;
}
InternalRemoveAt(value.ParentIndex);
return true;
}
/// <summary>
/// Removes the value at the specified index.
/// </summary>
public void RemoveAt(int index)
{
ArgumentOutOfRangeException.ThrowIfNegative(index);
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, InternalCount);
VerifyAPIReadWrite(_collection[index]);
InternalRemoveAt(index);
}
/// <summary>
/// Removes all IElements from the collection.
/// </summary>
public void Clear()
{
VerifyAPIReadWrite();
// Rather than clear, we swap out the FrugalStructList because
// we need to keep the old values around to notify the parent
// they were removed.
FrugalStructList<Visual3D> oldCollection = _collection;
_collection = new FrugalStructList<Visual3D>();
InvalidateEnumerators();
// NOTE: The collection must be updated before notifying the Visual.
for (int i = oldCollection.Count - 1; i >= 0; i--)
{
_owner.RemoveChild(oldCollection[i]);
}
Debug_ICC();
}
/// <summary>
/// Copies the IElements of the collection into "array" starting at "index"
/// </summary>
public void CopyTo(Visual3D[] array, int index)
{
VerifyAPIReadOnly();
ArgumentNullException.ThrowIfNull(array);
// The extra "index >= array.Length" check in because even if _collection.Count
// is 0 the index is not allowed to be equal or greater than the length
// (from the MSDN ICollection docs)
ArgumentOutOfRangeException.ThrowIfNegative(index);
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, array.Length);
ArgumentOutOfRangeException.ThrowIfGreaterThan(index, array.Length - Count);
_collection.CopyTo(array, index);
}
void ICollection.CopyTo(Array array, int index)
{
VerifyAPIReadOnly();
ArgumentNullException.ThrowIfNull(array);
// The extra "index >= array.Length" check in because even if _collection.Count
// is 0 the index is not allowed to be equal or greater than the length
// (from the MSDN ICollection docs)
ArgumentOutOfRangeException.ThrowIfNegative(index);
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, array.Length);
ArgumentOutOfRangeException.ThrowIfGreaterThan(index, array.Length - Count);
if (array.Rank != 1)
{
throw new ArgumentException(SR.Collection_BadRank);
}
// Elsewhere in the collection we throw an AE when the type is
// bad so we do it here as well to be consistent
try
{
int count = _collection.Count;
for (int i = 0; i < count; i++)
{
array.SetValue(_collection[i], index + i);
}
}
catch (InvalidCastException e)
{
throw new ArgumentException(SR.Format(SR.Collection_BadDestArray, "Visual3DCollection"), e);
}
}
/// <summary>
/// Determines if the list contains "value"
/// </summary>
public bool Contains(Visual3D value)
{
VerifyAPIReadOnly(value);
return (value != null && (value.InternalVisualParent == _owner));
}
/// <summary>
/// Returns the index of value in the list
/// </summary>
public int IndexOf(Visual3D value)
{
VerifyAPIReadOnly(value);
if (value == null || (value.InternalVisualParent != _owner))
{
return -1;
}
#pragma warning disable 56506 // Suppress presharp warning: Parameter 'value' to this public method must be validated: A null-dereference can occur here.
return value.ParentIndex;
#pragma warning restore 56506
}
/// <summary>
/// Returns an Enumerator for the collection.
/// </summary>
public Enumerator GetEnumerator()
{
VerifyAPIReadOnly();
return new Enumerator(this);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
IEnumerator<Visual3D> IEnumerable<Visual3D>.GetEnumerator()
{
return GetEnumerator();
}
#endregion Public Methods
//------------------------------------------------------
//
// Public Properties
//
//------------------------------------------------------
#region Public Properties
/// <summary>
/// Returns the IElement at the given index in the collection.
/// </summary>
public Visual3D this[int index]
{
get
{
VerifyAPIReadOnly();
return InternalGetItem(index);
}
set
{
ArgumentOutOfRangeException.ThrowIfNegative(index);
ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, InternalCount);
VerifyAPIForAdd(value);
InternalRemoveAt(index);
InternalInsert(index, value);
}
}
/// <summary>
/// The number of IElements contained in the collection.
/// </summary>
public int Count
{
get
{
VerifyAPIReadOnly();
return InternalCount;
}
}
bool ICollection.IsSynchronized
{
get
{
VerifyAPIReadOnly();
// True because we force single thread access via VerifyAccess()
return true;
}
}
object ICollection.SyncRoot
{
get
{
VerifyAPIReadOnly();
return _owner;
}
}
bool ICollection<Visual3D>.IsReadOnly
{
get
{
VerifyAPIReadOnly();
return false;
}
}
#region IList Members
/// <summary>
/// Adds an element to the Visual3DCollection
/// </summary>
int IList.Add(object value)
{
Add(Cast(value));
return InternalCount - 1;
}
/// <summary>
/// Determines whether an element is in the Visual3DCollection.
/// </summary>
bool IList.Contains(object value)
{
return Contains(value as Visual3D);
}
/// <summary>
/// Returns the index of the element in the Visual3DCollection
/// </summary>
int IList.IndexOf(object value)
{
return IndexOf(value as Visual3D);
}
/// <summary>
/// Inserts an element into the Visual3DCollection
/// </summary>
void IList.Insert(int index, object value)
{
Insert(index, Cast(value));
}
/// <summary>
/// </summary>
bool IList.IsFixedSize
{
get { return false; }
}
/// <summary>
/// </summary>
bool IList.IsReadOnly
{
get { return false; }
}
/// <summary>
/// Removes an element from the Visual3DCollection
/// </summary>
void IList.Remove(object value)
{
Remove(value as Visual3D);
}
/// <summary>
/// For more details, see <see cref="System.Windows.Media.VisualCollection" />
/// </summary>
object IList.this[int index]
{
get
{
return this[index];
}
set
{
this[index] = Cast(value);
}
}
#endregion
#endregion Public Properties
//------------------------------------------------------
//
// Public Events
//
//------------------------------------------------------
//------------------------------------------------------
//
// Internal Methods
//
//------------------------------------------------------
#region Internal Methods
internal Visual3D InternalGetItem(int index)
{
return _collection[index];
}
#endregion Internal Methods
//------------------------------------------------------
//
// Internal Properties
//
//------------------------------------------------------
#region Internal Properties
internal int InternalCount
{
get { return _collection.Count; }
}
#endregion Internal Properties
//------------------------------------------------------
//
// Private Methods
//
//------------------------------------------------------
#region Private Methods
private void VerifyAPIReadOnly()
{
Debug_ICC();
_owner.VerifyAPIReadOnly();
}
private void VerifyAPIReadOnly(Visual3D other)
{
Debug_ICC();
_owner.VerifyAPIReadOnly(other);
}
private void VerifyAPIReadWrite()
{
Debug_ICC();
_owner.VerifyAPIReadWrite();
}
private void VerifyAPIReadWrite(Visual3D other)
{
Debug_ICC();
_owner.VerifyAPIReadWrite(other);
}
private Visual3D Cast(object value)
{
ArgumentNullException.ThrowIfNull(value);
if (!(value is Visual3D))
{
throw new System.ArgumentException(SR.Format(SR.Collection_BadType, this.GetType().Name, value.GetType().Name, "Visual3D"));
}
return (Visual3D) value;
}
private void VerifyAPIForAdd(Visual3D value)
{
if (value == null)
{
throw new System.ArgumentException(SR.Collection_NoNull);
}
VerifyAPIReadWrite(value);
if (value.InternalVisualParent != null)
{
throw new System.ArgumentException(SR.VisualCollection_VisualHasParent);
}
}
private void InternalInsert(int index, Visual3D value)
{
_collection.Insert(index, value);
// Update ParentIndex value on each Visual3D. Run through them
// and increment. Note that this means that Inserting/Removal from a
// Visual3DCollection can be O(n^2) if done in the wrong order.
for (int i = index + 1, count = InternalCount; i < count; i++)
{
Debug.Assert(InternalGetItem(i).ParentIndex == i - 1,
"ParentIndex has been corrupted.");
InternalGetItem(i).ParentIndex = i;
}
InvalidateEnumerators();
// NOTE: The collection must be updated before notifying the Visual.
ConnectChild(index, value);
Debug_ICC();
}
private void InternalRemoveAt(int index)
{
Visual3D value = _collection[index];
_collection.RemoveAt(index);
// Update ParentIndices after the modified index are now invalid. Run through them
// and decrement. Note that this means that Inserting/Removal from a
// Visual3DCollection can be O(n^2) if done in the wrong order.
for (int i = index; i < InternalCount; i++)
{
Debug.Assert(InternalGetItem(i).ParentIndex == i + 1,
"ParentIndex has been corrupted.");
InternalGetItem(i).ParentIndex = i;
}
InvalidateEnumerators();
// NOTE: The collection must be updated before notifying the Visual.
_owner.RemoveChild(value);
Debug_ICC();
}
// Each member which modifies the collection should call this method to
// invalidate any enumerators which might have been handed out.
private void InvalidateEnumerators()
{
_version++;
}
#endregion Private Methods
//------------------------------------------------------
//
// DEBUG
//
//------------------------------------------------------
#region DEBUG
[Conditional("DEBUG")]
private void Debug_ICC()
{
Debug.Assert(_owner != null, "How did an Visual3DCollection get constructed without an owner?");
Dictionary<Visual3D, string> duplicates = new Dictionary<Visual3D, string>();
for (int i = 0; i < _collection.Count; i++)
{
Visual3D visual = _collection[i];
Debug.Assert(!duplicates.ContainsKey(visual), "How did the visual get re-inserted?");
duplicates.Add(visual, String.Empty);
Debug.Assert(visual.InternalVisualParent == _owner, "Why isn't our child's parent pointer the same as the collection owner?");
Debug.Assert(visual.ParentIndex == i,
String.Format(
CultureInfo.InvariantCulture,
"Child's ParentIndex does not match the child's actual position in the collection. Expected='{0}' Actual='{1}'",
i,
visual.ParentIndex));
// If the Visual3D is being added to the collection via a resource reference
// its inheritance context will be the owner of the ResourceDictionary in which
// it was declared. (Creating a 3D scene by loading loose xaml containing resources
// causes asserts to fail in Visual3DCollection.Debug_ICC())
//
// Debug.Assert(visual.InheritanceContext == _inheritanceContext,
// "How did a Visual3D get inserted without updating it's InheritanceContext?");
}
}
#endregion DEBUG
//------------------------------------------------------
//
// Private Fields
//
//------------------------------------------------------
#region Private Fields
private IVisual3DContainer _owner = null;
private FrugalStructList<Visual3D> _collection = new FrugalStructList<Visual3D>();
private int _version = 0;
#endregion Private Fields
//------------------------------------------------------
//
// Enumerator
//
//------------------------------------------------------
#region Enumerator
/// <summary>
/// VisualCollection Enumerator.
/// </summary>
public struct Enumerator : IEnumerator<Visual3D>, IEnumerator
{
#region Constructors
internal Enumerator(Visual3DCollection list)
{
Debug.Assert(list != null, "list may not be null.");
_list = list;
_index = -1;
_version = _list._version;
}
#endregion Constructors
#region Public Methods
/// <summary>
/// Advances the enumerator to the next IElement of the collection.
/// </summary>
public bool MoveNext()
{
if (_list._version != _version)
{
throw new InvalidOperationException(SR.Enumerator_CollectionChanged);
}
int count = _list.Count;
if (_index < count)
{
_index++;
}
return _index < count;
}
/// <summary>
/// Resets the enumerator to its initial position.
/// </summary>
public void Reset()
{
if (_list._version != _version)
{
throw new InvalidOperationException(SR.Enumerator_CollectionChanged);
}
_index = -1;
}
void IDisposable.Dispose()
{
// Do nothing - Required by the IEnumeable contract.
}
#endregion Public Methods
#region Public Properties
object IEnumerator.Current
{
get
{
return this.Current;
}
}
/// <summary>
/// Returns the current IElement.
/// </summary>
public Visual3D Current
{
#pragma warning disable 1634, 1691
#pragma warning disable 6503
get
{
if ((_index < 0) || (_index >= _list.Count))
{
throw new InvalidOperationException(SR.Enumerator_VerifyContext);
}
return _list[_index];
}
#pragma warning restore 6503
#pragma warning restore 1634, 1691
}
#endregion Public Methods
#region Private Fields
private Visual3DCollection _list;
private int _index;
private int _version;
#endregion Private Fields
}
#endregion Enumerator
}
}
|