Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Core / CSharp / System / Windows / FreezableCollection.cs / 1 / FreezableCollection.cs
//----------------------------------------------------------------------------
//
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//
// Description: This file contains the implementation of FreezableCollection.
// FreezableCollection is an IList implementation which implements
// the requisite infrastructure for collections of DependencyObjects,
// Freezables, and Animatables and which is itself an Animatable and a Freezable.
//
//---------------------------------------------------------------------------
using MS.Internal;
using MS.Internal.KnownBoxes;
using MS.Internal.Collections;
using MS.Internal.PresentationCore;
using MS.Utility;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.InteropServices;
using System.ComponentModel.Design.Serialization;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Effects;
using System.Windows.Media.Media3D;
using System.Windows.Media.Animation;
using System.Windows.Media.Composition;
using System.Windows.Media.Imaging;
using System.Windows.Markup;
using System.Windows.Media.Converters;
using System.Security;
using System.Security.Permissions;
using SR=MS.Internal.PresentationCore.SR;
using SRID=MS.Internal.PresentationCore.SRID;
// These types are aliased to match the unamanaged names used in interop
using BOOL = System.UInt32;
using WORD = System.UInt16;
using Float = System.Single;
namespace System.Windows
{
///
/// FreezableCollection<T> is an IList<T> implementation which implements
/// the requisite infrastructure for collections of DependencyObjects,
/// Freezables, and Animatables and which is itself an Animatable and a Freezable.
///
public class FreezableCollection: Animatable, IList, IList, INotifyCollectionChanged, INotifyPropertyChanged
where T: DependencyObject
{
#region Constructors
//-----------------------------------------------------
//
// Constructors
//
//-----------------------------------------------------
///
/// Initializes a new instance that is empty.
///
public FreezableCollection()
{
_collection = new List();
}
///
/// Initializes a new instance that is empty and has the specified initial capacity.
///
/// int - The number of elements that the new list is initially capable of storing.
public FreezableCollection(int capacity)
{
_collection = new List(capacity);
}
///
/// Creates a FreezableCollection<T> with all of the same elements as "collection"
///
public FreezableCollection(IEnumerable collection)
{
// The WritePreamble and WritePostscript aren't technically necessary
// in the constructor as of 1/20/05 but they are put here in case
// their behavior changes at a later date
WritePreamble();
if (collection != null)
{
int count = GetCount(collection);
if (count > 0)
{
_collection = new List(count);
}
else
{
_collection = new List();
}
foreach (T item in collection)
{
if (item == null)
{
throw new System.ArgumentException(SR.Get(SRID.Collection_NoNull));
}
OnFreezablePropertyChanged(/* oldValue = */ null, item);
_collection.Add(item);
}
WritePostscript();
}
else
{
throw new ArgumentNullException("collection");
}
}
#endregion Constructors
//------------------------------------------------------
//
// Public Methods
//
//-----------------------------------------------------
#region Public Methods
///
/// Shadows inherited Clone() with a strongly typed
/// version for convenience.
///
public new FreezableCollection Clone()
{
return (FreezableCollection)base.Clone();
}
///
/// Shadows inherited CloneCurrentValue() with a strongly typed
/// version for convenience.
///
public new FreezableCollection CloneCurrentValue()
{
return (FreezableCollection)base.CloneCurrentValue();
}
#endregion Public Methods
//------------------------------------------------------
//
// Public Properties
//
//------------------------------------------------------
#region IList
///
/// Adds "value" to the list
///
public void Add(T value)
{
AddHelper(value);
}
///
/// Removes all elements from the list
///
public void Clear()
{
CheckReentrancy();
WritePreamble();
for (int i = _collection.Count - 1; i >= 0; i--)
{
OnFreezablePropertyChanged(/* oldValue = */ _collection[i], /* newValue = */ null);
}
_collection.Clear();
Debug.Assert(_collection.Count == 0);
++_version;
WritePostscript();
OnCollectionChanged(NotifyCollectionChangedAction.Reset, 0, null, 0, null);
}
///
/// Determines if the list contains "value"
///
public bool Contains(T value)
{
ReadPreamble();
return _collection.Contains(value);
}
///
/// Returns the index of "value" in the list
///
public int IndexOf(T value)
{
ReadPreamble();
return _collection.IndexOf(value);
}
///
/// Inserts "value" into the list at the specified position
///
public void Insert(int index, T value)
{
if (value == null)
{
throw new System.ArgumentException(SR.Get(SRID.Collection_NoNull));
}
CheckReentrancy();
WritePreamble();
OnFreezablePropertyChanged(/* oldValue = */ null, /* newValue = */ value);
_collection.Insert(index, value);
++_version;
WritePostscript();
OnCollectionChanged(NotifyCollectionChangedAction.Add, 0, null, index, value);
}
///
/// Removes "value" from the list
///
public bool Remove(T value)
{
WritePreamble();
// By design collections "succeed silently" if you attempt to remove an item
// not in the collection. Therefore we need to first verify the old value exists
// before calling OnFreezablePropertyChanged. Since we already need to locate
// the item in the collection we keep the index and use RemoveAt(...) to do
// the work. (Windows OS #1016178)
// We use the public IndexOf to guard our UIContext since OnFreezablePropertyChanged
// is only called conditionally. IList.IndexOf returns -1 if the value is not found.
int index = IndexOf(value);
if (index >= 0)
{
CheckReentrancy();
T oldValue = _collection[index];
OnFreezablePropertyChanged(oldValue, null);
// we already have index from IndexOf so instead of using Remove -
// which will search the collection a second time - we'll use RemoveAt
_collection.RemoveAt(index);
++_version;
WritePostscript();
OnCollectionChanged(NotifyCollectionChangedAction.Remove, index, oldValue, 0, null);
return true;
}
// Collection_Remove returns true, calls WritePostscript,
// increments version, and does UpdateResource if it succeeds
return false;
}
///
/// Removes the element at the specified index
///
public void RemoveAt(int index)
{
T oldValue = _collection[ index ];
RemoveAtWithoutFiringPublicEvents(index);
// RemoveAtWithoutFiringPublicEvents incremented the version
WritePostscript();
OnCollectionChanged(NotifyCollectionChangedAction.Remove, index, oldValue, 0, null);
}
///
/// Removes the element at the specified index without firing
/// the public Changed event.
/// The caller - typically a public method - is responsible for calling
/// WritePostscript if appropriate.
///
internal void RemoveAtWithoutFiringPublicEvents(int index)
{
CheckReentrancy();
WritePreamble();
T oldValue = _collection[ index ];
OnFreezablePropertyChanged(oldValue, null);
_collection.RemoveAt(index);
++_version;
// No WritePostScript to avoid firing the Changed event.
}
///
/// Indexer for the collection
///
public T this[int index]
{
get
{
ReadPreamble();
return _collection[index];
}
set
{
if (value == null)
{
throw new System.ArgumentException(SR.Get(SRID.Collection_NoNull));
}
CheckReentrancy();
WritePreamble();
T oldValue = _collection[ index ];
bool isChanging = !Object.ReferenceEquals(oldValue, value);
if (isChanging)
{
OnFreezablePropertyChanged(oldValue, value);
_collection[ index ] = value;
}
++_version;
WritePostscript();
if (isChanging)
{
OnCollectionChanged(NotifyCollectionChangedAction.Replace, index, oldValue, index, value);
}
}
}
#endregion
#region ICollection
///
/// The number of elements contained in the collection.
///
public int Count
{
get
{
ReadPreamble();
return _collection.Count;
}
}
///
/// Copies the elements of the collection into "array" starting at "index"
///
public void CopyTo(T[] array, int index)
{
ReadPreamble();
if (array == null)
{
throw new ArgumentNullException("array");
}
// This will not throw in the case that we are copying
// from an empty collection. This is consistent with the
// BCL Collection implementations. (Windows 1587365)
if (index < 0 || (index + _collection.Count) > array.Length)
{
throw new ArgumentOutOfRangeException("index");
}
_collection.CopyTo(array, index);
}
bool ICollection.IsReadOnly
{
get
{
ReadPreamble();
return IsFrozen;
}
}
#endregion
#region IEnumerable
///
/// Returns an enumerator for the collection
///
public Enumerator GetEnumerator()
{
ReadPreamble();
return new Enumerator(this);
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
#region IList
bool IList.IsReadOnly
{
get
{
return ((ICollection)this).IsReadOnly;
}
}
bool IList.IsFixedSize
{
get
{
ReadPreamble();
return IsFrozen;
}
}
object IList.this[int index]
{
get
{
return this[index];
}
set
{
// Forwards to typed implementation
this[index] = Cast(value);
}
}
int IList.Add(object value)
{
// Forward to typed helper
return AddHelper(Cast(value));
}
bool IList.Contains(object value)
{
return Contains(value as T);
}
int IList.IndexOf(object value)
{
return IndexOf(value as T);
}
void IList.Insert(int index, object value)
{
// Forward to IList Insert
Insert(index, Cast(value));
}
void IList.Remove(object value)
{
Remove(value as T);
}
#endregion
#region ICollection
void ICollection.CopyTo(Array array, int index)
{
ReadPreamble();
if (array == null)
{
throw new ArgumentNullException("array");
}
// This will not throw in the case that we are copying
// from an empty collection. This is consistent with the
// BCL Collection implementations. (Windows 1587365)
if (index < 0 || (index + _collection.Count) > array.Length)
{
throw new ArgumentOutOfRangeException("index");
}
if (array.Rank != 1)
{
throw new ArgumentException(SR.Get(SRID.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.Get(SRID.Collection_BadDestArray, this.GetType().Name), e);
}
}
bool ICollection.IsSynchronized
{
get
{
ReadPreamble();
return IsFrozen || Dispatcher != null;
}
}
object ICollection.SyncRoot
{
get
{
ReadPreamble();
return this;
}
}
#endregion
#region IEnumerable
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
#region INotifyCollectionChanged
///
/// CollectionChanged event (per ).
///
event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged
{
add
{
CollectionChanged += value;
}
remove
{
CollectionChanged -= value;
}
}
///
/// Occurs when the collection changes, either by adding or removing an item.
///
///
/// see
///
private event NotifyCollectionChangedEventHandler CollectionChanged;
///
/// Raise CollectionChanged event to any listeners.
/// Properties/methods modifying this FreezableCollection will raise
/// a collection changed event through this method.
///
private void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
{
using (BlockReentrancy())
{
CollectionChanged(this, e);
}
}
}
#endregion INotifyCollectionChanged
#region INotifyPropertyChanged
///
/// PropertyChanged event (per ).
///
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
{
add
{
PrivatePropertyChanged += value;
}
remove
{
PrivatePropertyChanged -= value;
}
}
///
/// PropertyChanged event (per ).
///
// We can't call this "PropertyChanged" because the base class Animatable
// declares an internal method with that name.
private event PropertyChangedEventHandler PrivatePropertyChanged;
///
/// Raises a PropertyChanged event (per ).
///
private void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PrivatePropertyChanged != null)
{
PrivatePropertyChanged(this, e);
}
}
#endregion INotifyPropertyChanged
#region Internal Helpers
///
/// Freezable collections need to notify their contained Freezables
/// about the change in the InheritanceContext
///
internal override void OnInheritanceContextChangedCore(EventArgs args)
{
base.OnInheritanceContextChangedCore(args);
for (int i=0; i by sniffing for the
// ICollection and ICollection interfaces. If the count can not be
// extract it return -1.
private int GetCount(IEnumerable enumerable)
{
ICollection collectionAsICollection = enumerable as ICollection;
if (collectionAsICollection != null)
{
return collectionAsICollection.Count;
}
ICollection enumerableAsICollectionT = enumerable as ICollection;
if (enumerableAsICollectionT != null)
{
return enumerableAsICollectionT.Count;
}
// We return -1 here and force the caller to decide how to handle
// the unknown case. In the future different collections might
// use different estimates for unknown. (e.g., Point3DCollections
// tend to be 8+ while DoubleCollections are freqently <= 2, etc.)
return -1;
}
// IList.Add returns int and IList.Add does not. This
// is called by both Adds and IList's just ignores the
// integer
private int AddHelper(T value)
{
CheckReentrancy();
int index = AddWithoutFiringPublicEvents(value);
// AddAtWithoutFiringPublicEvents incremented the version
WritePostscript();
//
OnCollectionChanged(NotifyCollectionChangedAction.Add, 0, null, index-1, value);
return index;
}
internal int AddWithoutFiringPublicEvents(T value)
{
if (value == null)
{
throw new System.ArgumentException(SR.Get(SRID.Collection_NoNull));
}
WritePreamble();
T newValue = value;
OnFreezablePropertyChanged(/* oldValue = */ null, newValue);
_collection.Add(value);
++_version;
// No WritePostScript to avoid firing the Changed event.
//
return _collection.Count;
}
// helper to raise events after the collection has changed
private void OnCollectionChanged(NotifyCollectionChangedAction action,
int oldIndex,
T oldValue,
int newIndex,
T newValue)
{
if (PrivatePropertyChanged == null && CollectionChanged == null)
return;
using (BlockReentrancy())
{
// most collection changes imply a change in the Count and indexer
// properties
if (PrivatePropertyChanged != null)
{
if (action != NotifyCollectionChangedAction.Replace &&
action != NotifyCollectionChangedAction.Move)
{
OnPropertyChanged(new PropertyChangedEventArgs(CountPropertyName));
}
OnPropertyChanged(new PropertyChangedEventArgs(IndexerPropertyName));
}
if (CollectionChanged != null)
{
NotifyCollectionChangedEventArgs args;
switch (action)
{
case NotifyCollectionChangedAction.Reset:
args = new NotifyCollectionChangedEventArgs(action);
break;
case NotifyCollectionChangedAction.Add:
args = new NotifyCollectionChangedEventArgs(action, newValue, newIndex);
break;
case NotifyCollectionChangedAction.Remove:
args = new NotifyCollectionChangedEventArgs(action, oldValue, oldIndex);
break;
case NotifyCollectionChangedAction.Replace:
args = new NotifyCollectionChangedEventArgs(action, newValue, oldValue, newIndex);
break;
default:
//
throw new InvalidOperationException("Unknown/unexpected change event");
}
OnCollectionChanged(args);
}
}
}
#endregion Private Helpers
//-----------------------------------------------------
//
// Protected Methods
//
//------------------------------------------------------
#region Protected Methods
///
/// Implementation of Freezable.CreateInstanceCore .
///
/// The new Freezable.
protected override Freezable CreateInstanceCore()
{
return new FreezableCollection();
}
enum CloneCommonType
{
Clone,
CloneCurrentValue,
GetAsFrozen,
GetCurrentValueAsFrozen
}
private void CloneCommon(FreezableCollection source,
CloneCommonType cloneType)
{
int count = source._collection.Count;
_collection = new List(count);
for (int i = 0; i < count; i++)
{
T newValue = source._collection[i];
Freezable itemAsFreezable = newValue as Freezable;
if (itemAsFreezable != null)
{
switch (cloneType)
{
case CloneCommonType.Clone:
newValue = itemAsFreezable.Clone() as T;
break;
case CloneCommonType.CloneCurrentValue:
newValue = itemAsFreezable.CloneCurrentValue() as T;
break;
case CloneCommonType.GetAsFrozen:
newValue = itemAsFreezable.GetAsFrozen() as T;
break;
case CloneCommonType.GetCurrentValueAsFrozen:
newValue = itemAsFreezable.GetCurrentValueAsFrozen() as T;
break;
default:
Invariant.Assert(false, "Invalid CloneCommonType encountered.");
break;
}
if (newValue == null)
{
throw new InvalidOperationException(SR.Get(SRID.Freezable_CloneInvalidType, typeof(T).Name));
}
}
OnFreezablePropertyChanged(/* oldValue = */ null, newValue);
_collection.Add(newValue);
}
}
///
/// Implementation of Freezable.CloneCore()
///
protected override void CloneCore(Freezable source)
{
base.CloneCore(source);
FreezableCollection sourceFreezableCollection = (FreezableCollection) source;
CloneCommon(sourceFreezableCollection, CloneCommonType.Clone);
}
///
/// Implementation of Freezable.CloneCurrentValueCore()
///
protected override void CloneCurrentValueCore(Freezable source)
{
base.CloneCurrentValueCore(source);
FreezableCollection sourceFreezableCollection = (FreezableCollection) source;
CloneCommon(sourceFreezableCollection, CloneCommonType.CloneCurrentValue);
}
///
/// Implementation of Freezable.GetAsFrozenCore()
///
protected override void GetAsFrozenCore(Freezable source)
{
base.GetAsFrozenCore(source);
FreezableCollection sourceFreezableCollection = (FreezableCollection) source;
CloneCommon(sourceFreezableCollection, CloneCommonType.GetAsFrozen);
}
///
/// Implementation of Freezable.GetCurrentValueAsFrozenCore()
///
protected override void GetCurrentValueAsFrozenCore(Freezable source)
{
base.GetCurrentValueAsFrozenCore(source);
FreezableCollection sourceFreezableCollection = (FreezableCollection) source;
CloneCommon(sourceFreezableCollection, CloneCommonType.GetCurrentValueAsFrozen);
}
///
/// Implementation of Freezable.FreezeCore .
///
protected override bool FreezeCore(bool isChecking)
{
bool canFreeze = base.FreezeCore(isChecking);
int count = _collection.Count;
for (int i = 0; i < count && canFreeze; i++)
{
T item = _collection[i];
Freezable itemAsFreezable = item as Freezable;
if (itemAsFreezable != null)
{
canFreeze &= Freezable.Freeze(itemAsFreezable, isChecking);
}
else
{
canFreeze &= (item.Dispatcher == null);
}
}
return canFreeze;
}
///
/// Disallow reentrant attempts to change this collection. E.g. a event handler
/// of the CollectionChanged event is not allowed to make changes to this collection.
///
///
/// typical usage is to wrap e.g. a OnCollectionChanged call with a using() scope:
///
/// using (BlockReentrancy())
/// {
/// CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, item, index));
/// }
///
///
private IDisposable BlockReentrancy()
{
_monitor.Enter();
return _monitor;
}
/// Check and assert for reentrant attempts to change this collection.
/// raised when changing the collection
/// while another collection change is still being notified to other listeners
private void CheckReentrancy()
{
if (_monitor.Busy)
{
//
throw new InvalidOperationException("Cannot change FreezableCollection during a CollectionChanged event.");
}
}
#endregion ProtectedMethods
//-----------------------------------------------------
//
// Internal Fields
//
//-----------------------------------------------------
#region Internal Fields
internal List _collection;
internal uint _version = 0;
#endregion Internal Fields
//-----------------------------------------------------
//
// Private Fields
//
//------------------------------------------------------
#region Private Fields
private const string CountPropertyName = "Count";
// This must agree with Binding.IndexerName. It is declared separately
// here so as to avoid a dependency on PresentationFramework.dll.
private const string IndexerPropertyName = "Item[]";
private SimpleMonitor _monitor = new SimpleMonitor();
#endregion Private Fields
#region Enumerator
///
/// Enumerates the items in a TCollection
///
public struct Enumerator : IEnumerator, IEnumerator
{
#region Constructor
internal Enumerator(FreezableCollection list)
{
Debug.Assert(list != null, "list may not be null.");
_list = list;
_version = list._version;
_index = -1;
_current = default(T);
}
#endregion
#region Methods
void IDisposable.Dispose()
{
}
///
/// Advances the enumerator to the next element of the collection.
///
///
/// true if the enumerator was successfully advanced to the next element,
/// false if the enumerator has passed the end of the collection.
///
public bool MoveNext()
{
_list.ReadPreamble();
if (_version == _list._version)
{
if (_index > -2 && _index < _list._collection.Count - 1)
{
_current = _list._collection[++_index];
return true;
}
else
{
_index = -2; // -2 indicates "past the end"
return false;
}
}
else
{
throw new InvalidOperationException(SR.Get(SRID.Enumerator_CollectionChanged));
}
}
///
/// Sets the enumerator to its initial position, which is before the
/// first element in the collection.
///
public void Reset()
{
_list.ReadPreamble();
if (_version == _list._version)
{
_index = -1;
}
else
{
throw new InvalidOperationException(SR.Get(SRID.Enumerator_CollectionChanged));
}
}
#endregion
#region Properties
object IEnumerator.Current
{
get
{
return this.Current;
}
}
///
/// Current element
///
/// The behavior of IEnumerable<T>.Current is undefined
/// before the first MoveNext and after we have walked
/// off the end of the list. However, the IEnumerable.Current
/// contract requires that we throw exceptions
///
public T Current
{
get
{
if (_index > -1)
{
return _current;
}
else if (_index == -1)
{
throw new InvalidOperationException(SR.Get(SRID.Enumerator_NotStarted));
}
else
{
Debug.Assert(_index == -2, "expected -2, got " + _index + "\n");
throw new InvalidOperationException(SR.Get(SRID.Enumerator_ReachedEnd));
}
}
}
#endregion
#region Data
private T _current;
private FreezableCollection _list;
private uint _version;
private int _index;
#endregion
}
private class SimpleMonitor : IDisposable
{
public void Enter()
{
++ _busyCount;
}
public void Dispose()
{
-- _busyCount;
}
public bool Busy { get { return _busyCount > 0; } }
int _busyCount;
}
#endregion
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
//----------------------------------------------------------------------------
//
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//
// Description: This file contains the implementation of FreezableCollection.
// FreezableCollection is an IList implementation which implements
// the requisite infrastructure for collections of DependencyObjects,
// Freezables, and Animatables and which is itself an Animatable and a Freezable.
//
//---------------------------------------------------------------------------
using MS.Internal;
using MS.Internal.KnownBoxes;
using MS.Internal.Collections;
using MS.Internal.PresentationCore;
using MS.Utility;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.InteropServices;
using System.ComponentModel.Design.Serialization;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Effects;
using System.Windows.Media.Media3D;
using System.Windows.Media.Animation;
using System.Windows.Media.Composition;
using System.Windows.Media.Imaging;
using System.Windows.Markup;
using System.Windows.Media.Converters;
using System.Security;
using System.Security.Permissions;
using SR=MS.Internal.PresentationCore.SR;
using SRID=MS.Internal.PresentationCore.SRID;
// These types are aliased to match the unamanaged names used in interop
using BOOL = System.UInt32;
using WORD = System.UInt16;
using Float = System.Single;
namespace System.Windows
{
///
/// FreezableCollection<T> is an IList<T> implementation which implements
/// the requisite infrastructure for collections of DependencyObjects,
/// Freezables, and Animatables and which is itself an Animatable and a Freezable.
///
public class FreezableCollection: Animatable, IList, IList, INotifyCollectionChanged, INotifyPropertyChanged
where T: DependencyObject
{
#region Constructors
//-----------------------------------------------------
//
// Constructors
//
//-----------------------------------------------------
///
/// Initializes a new instance that is empty.
///
public FreezableCollection()
{
_collection = new List();
}
///
/// Initializes a new instance that is empty and has the specified initial capacity.
///
/// int - The number of elements that the new list is initially capable of storing.
public FreezableCollection(int capacity)
{
_collection = new List(capacity);
}
///
/// Creates a FreezableCollection<T> with all of the same elements as "collection"
///
public FreezableCollection(IEnumerable collection)
{
// The WritePreamble and WritePostscript aren't technically necessary
// in the constructor as of 1/20/05 but they are put here in case
// their behavior changes at a later date
WritePreamble();
if (collection != null)
{
int count = GetCount(collection);
if (count > 0)
{
_collection = new List(count);
}
else
{
_collection = new List();
}
foreach (T item in collection)
{
if (item == null)
{
throw new System.ArgumentException(SR.Get(SRID.Collection_NoNull));
}
OnFreezablePropertyChanged(/* oldValue = */ null, item);
_collection.Add(item);
}
WritePostscript();
}
else
{
throw new ArgumentNullException("collection");
}
}
#endregion Constructors
//------------------------------------------------------
//
// Public Methods
//
//-----------------------------------------------------
#region Public Methods
///
/// Shadows inherited Clone() with a strongly typed
/// version for convenience.
///
public new FreezableCollection Clone()
{
return (FreezableCollection)base.Clone();
}
///
/// Shadows inherited CloneCurrentValue() with a strongly typed
/// version for convenience.
///
public new FreezableCollection CloneCurrentValue()
{
return (FreezableCollection)base.CloneCurrentValue();
}
#endregion Public Methods
//------------------------------------------------------
//
// Public Properties
//
//------------------------------------------------------
#region IList
///
/// Adds "value" to the list
///
public void Add(T value)
{
AddHelper(value);
}
///
/// Removes all elements from the list
///
public void Clear()
{
CheckReentrancy();
WritePreamble();
for (int i = _collection.Count - 1; i >= 0; i--)
{
OnFreezablePropertyChanged(/* oldValue = */ _collection[i], /* newValue = */ null);
}
_collection.Clear();
Debug.Assert(_collection.Count == 0);
++_version;
WritePostscript();
OnCollectionChanged(NotifyCollectionChangedAction.Reset, 0, null, 0, null);
}
///
/// Determines if the list contains "value"
///
public bool Contains(T value)
{
ReadPreamble();
return _collection.Contains(value);
}
///
/// Returns the index of "value" in the list
///
public int IndexOf(T value)
{
ReadPreamble();
return _collection.IndexOf(value);
}
///
/// Inserts "value" into the list at the specified position
///
public void Insert(int index, T value)
{
if (value == null)
{
throw new System.ArgumentException(SR.Get(SRID.Collection_NoNull));
}
CheckReentrancy();
WritePreamble();
OnFreezablePropertyChanged(/* oldValue = */ null, /* newValue = */ value);
_collection.Insert(index, value);
++_version;
WritePostscript();
OnCollectionChanged(NotifyCollectionChangedAction.Add, 0, null, index, value);
}
///
/// Removes "value" from the list
///
public bool Remove(T value)
{
WritePreamble();
// By design collections "succeed silently" if you attempt to remove an item
// not in the collection. Therefore we need to first verify the old value exists
// before calling OnFreezablePropertyChanged. Since we already need to locate
// the item in the collection we keep the index and use RemoveAt(...) to do
// the work. (Windows OS #1016178)
// We use the public IndexOf to guard our UIContext since OnFreezablePropertyChanged
// is only called conditionally. IList.IndexOf returns -1 if the value is not found.
int index = IndexOf(value);
if (index >= 0)
{
CheckReentrancy();
T oldValue = _collection[index];
OnFreezablePropertyChanged(oldValue, null);
// we already have index from IndexOf so instead of using Remove -
// which will search the collection a second time - we'll use RemoveAt
_collection.RemoveAt(index);
++_version;
WritePostscript();
OnCollectionChanged(NotifyCollectionChangedAction.Remove, index, oldValue, 0, null);
return true;
}
// Collection_Remove returns true, calls WritePostscript,
// increments version, and does UpdateResource if it succeeds
return false;
}
///
/// Removes the element at the specified index
///
public void RemoveAt(int index)
{
T oldValue = _collection[ index ];
RemoveAtWithoutFiringPublicEvents(index);
// RemoveAtWithoutFiringPublicEvents incremented the version
WritePostscript();
OnCollectionChanged(NotifyCollectionChangedAction.Remove, index, oldValue, 0, null);
}
///
/// Removes the element at the specified index without firing
/// the public Changed event.
/// The caller - typically a public method - is responsible for calling
/// WritePostscript if appropriate.
///
internal void RemoveAtWithoutFiringPublicEvents(int index)
{
CheckReentrancy();
WritePreamble();
T oldValue = _collection[ index ];
OnFreezablePropertyChanged(oldValue, null);
_collection.RemoveAt(index);
++_version;
// No WritePostScript to avoid firing the Changed event.
}
///
/// Indexer for the collection
///
public T this[int index]
{
get
{
ReadPreamble();
return _collection[index];
}
set
{
if (value == null)
{
throw new System.ArgumentException(SR.Get(SRID.Collection_NoNull));
}
CheckReentrancy();
WritePreamble();
T oldValue = _collection[ index ];
bool isChanging = !Object.ReferenceEquals(oldValue, value);
if (isChanging)
{
OnFreezablePropertyChanged(oldValue, value);
_collection[ index ] = value;
}
++_version;
WritePostscript();
if (isChanging)
{
OnCollectionChanged(NotifyCollectionChangedAction.Replace, index, oldValue, index, value);
}
}
}
#endregion
#region ICollection
///
/// The number of elements contained in the collection.
///
public int Count
{
get
{
ReadPreamble();
return _collection.Count;
}
}
///
/// Copies the elements of the collection into "array" starting at "index"
///
public void CopyTo(T[] array, int index)
{
ReadPreamble();
if (array == null)
{
throw new ArgumentNullException("array");
}
// This will not throw in the case that we are copying
// from an empty collection. This is consistent with the
// BCL Collection implementations. (Windows 1587365)
if (index < 0 || (index + _collection.Count) > array.Length)
{
throw new ArgumentOutOfRangeException("index");
}
_collection.CopyTo(array, index);
}
bool ICollection.IsReadOnly
{
get
{
ReadPreamble();
return IsFrozen;
}
}
#endregion
#region IEnumerable
///
/// Returns an enumerator for the collection
///
public Enumerator GetEnumerator()
{
ReadPreamble();
return new Enumerator(this);
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
#region IList
bool IList.IsReadOnly
{
get
{
return ((ICollection)this).IsReadOnly;
}
}
bool IList.IsFixedSize
{
get
{
ReadPreamble();
return IsFrozen;
}
}
object IList.this[int index]
{
get
{
return this[index];
}
set
{
// Forwards to typed implementation
this[index] = Cast(value);
}
}
int IList.Add(object value)
{
// Forward to typed helper
return AddHelper(Cast(value));
}
bool IList.Contains(object value)
{
return Contains(value as T);
}
int IList.IndexOf(object value)
{
return IndexOf(value as T);
}
void IList.Insert(int index, object value)
{
// Forward to IList Insert
Insert(index, Cast(value));
}
void IList.Remove(object value)
{
Remove(value as T);
}
#endregion
#region ICollection
void ICollection.CopyTo(Array array, int index)
{
ReadPreamble();
if (array == null)
{
throw new ArgumentNullException("array");
}
// This will not throw in the case that we are copying
// from an empty collection. This is consistent with the
// BCL Collection implementations. (Windows 1587365)
if (index < 0 || (index + _collection.Count) > array.Length)
{
throw new ArgumentOutOfRangeException("index");
}
if (array.Rank != 1)
{
throw new ArgumentException(SR.Get(SRID.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.Get(SRID.Collection_BadDestArray, this.GetType().Name), e);
}
}
bool ICollection.IsSynchronized
{
get
{
ReadPreamble();
return IsFrozen || Dispatcher != null;
}
}
object ICollection.SyncRoot
{
get
{
ReadPreamble();
return this;
}
}
#endregion
#region IEnumerable
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
#region INotifyCollectionChanged
///
/// CollectionChanged event (per ).
///
event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged
{
add
{
CollectionChanged += value;
}
remove
{
CollectionChanged -= value;
}
}
///
/// Occurs when the collection changes, either by adding or removing an item.
///
///
/// see
///
private event NotifyCollectionChangedEventHandler CollectionChanged;
///
/// Raise CollectionChanged event to any listeners.
/// Properties/methods modifying this FreezableCollection will raise
/// a collection changed event through this method.
///
private void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
{
using (BlockReentrancy())
{
CollectionChanged(this, e);
}
}
}
#endregion INotifyCollectionChanged
#region INotifyPropertyChanged
///
/// PropertyChanged event (per ).
///
event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
{
add
{
PrivatePropertyChanged += value;
}
remove
{
PrivatePropertyChanged -= value;
}
}
///
/// PropertyChanged event (per ).
///
// We can't call this "PropertyChanged" because the base class Animatable
// declares an internal method with that name.
private event PropertyChangedEventHandler PrivatePropertyChanged;
///
/// Raises a PropertyChanged event (per ).
///
private void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PrivatePropertyChanged != null)
{
PrivatePropertyChanged(this, e);
}
}
#endregion INotifyPropertyChanged
#region Internal Helpers
///
/// Freezable collections need to notify their contained Freezables
/// about the change in the InheritanceContext
///
internal override void OnInheritanceContextChangedCore(EventArgs args)
{
base.OnInheritanceContextChangedCore(args);
for (int i=0; i by sniffing for the
// ICollection and ICollection interfaces. If the count can not be
// extract it return -1.
private int GetCount(IEnumerable enumerable)
{
ICollection collectionAsICollection = enumerable as ICollection;
if (collectionAsICollection != null)
{
return collectionAsICollection.Count;
}
ICollection enumerableAsICollectionT = enumerable as ICollection;
if (enumerableAsICollectionT != null)
{
return enumerableAsICollectionT.Count;
}
// We return -1 here and force the caller to decide how to handle
// the unknown case. In the future different collections might
// use different estimates for unknown. (e.g., Point3DCollections
// tend to be 8+ while DoubleCollections are freqently <= 2, etc.)
return -1;
}
// IList.Add returns int and IList.Add does not. This
// is called by both Adds and IList's just ignores the
// integer
private int AddHelper(T value)
{
CheckReentrancy();
int index = AddWithoutFiringPublicEvents(value);
// AddAtWithoutFiringPublicEvents incremented the version
WritePostscript();
//
OnCollectionChanged(NotifyCollectionChangedAction.Add, 0, null, index-1, value);
return index;
}
internal int AddWithoutFiringPublicEvents(T value)
{
if (value == null)
{
throw new System.ArgumentException(SR.Get(SRID.Collection_NoNull));
}
WritePreamble();
T newValue = value;
OnFreezablePropertyChanged(/* oldValue = */ null, newValue);
_collection.Add(value);
++_version;
// No WritePostScript to avoid firing the Changed event.
//
return _collection.Count;
}
// helper to raise events after the collection has changed
private void OnCollectionChanged(NotifyCollectionChangedAction action,
int oldIndex,
T oldValue,
int newIndex,
T newValue)
{
if (PrivatePropertyChanged == null && CollectionChanged == null)
return;
using (BlockReentrancy())
{
// most collection changes imply a change in the Count and indexer
// properties
if (PrivatePropertyChanged != null)
{
if (action != NotifyCollectionChangedAction.Replace &&
action != NotifyCollectionChangedAction.Move)
{
OnPropertyChanged(new PropertyChangedEventArgs(CountPropertyName));
}
OnPropertyChanged(new PropertyChangedEventArgs(IndexerPropertyName));
}
if (CollectionChanged != null)
{
NotifyCollectionChangedEventArgs args;
switch (action)
{
case NotifyCollectionChangedAction.Reset:
args = new NotifyCollectionChangedEventArgs(action);
break;
case NotifyCollectionChangedAction.Add:
args = new NotifyCollectionChangedEventArgs(action, newValue, newIndex);
break;
case NotifyCollectionChangedAction.Remove:
args = new NotifyCollectionChangedEventArgs(action, oldValue, oldIndex);
break;
case NotifyCollectionChangedAction.Replace:
args = new NotifyCollectionChangedEventArgs(action, newValue, oldValue, newIndex);
break;
default:
//
throw new InvalidOperationException("Unknown/unexpected change event");
}
OnCollectionChanged(args);
}
}
}
#endregion Private Helpers
//-----------------------------------------------------
//
// Protected Methods
//
//------------------------------------------------------
#region Protected Methods
///
/// Implementation of Freezable.CreateInstanceCore .
///
/// The new Freezable.
protected override Freezable CreateInstanceCore()
{
return new FreezableCollection();
}
enum CloneCommonType
{
Clone,
CloneCurrentValue,
GetAsFrozen,
GetCurrentValueAsFrozen
}
private void CloneCommon(FreezableCollection source,
CloneCommonType cloneType)
{
int count = source._collection.Count;
_collection = new List(count);
for (int i = 0; i < count; i++)
{
T newValue = source._collection[i];
Freezable itemAsFreezable = newValue as Freezable;
if (itemAsFreezable != null)
{
switch (cloneType)
{
case CloneCommonType.Clone:
newValue = itemAsFreezable.Clone() as T;
break;
case CloneCommonType.CloneCurrentValue:
newValue = itemAsFreezable.CloneCurrentValue() as T;
break;
case CloneCommonType.GetAsFrozen:
newValue = itemAsFreezable.GetAsFrozen() as T;
break;
case CloneCommonType.GetCurrentValueAsFrozen:
newValue = itemAsFreezable.GetCurrentValueAsFrozen() as T;
break;
default:
Invariant.Assert(false, "Invalid CloneCommonType encountered.");
break;
}
if (newValue == null)
{
throw new InvalidOperationException(SR.Get(SRID.Freezable_CloneInvalidType, typeof(T).Name));
}
}
OnFreezablePropertyChanged(/* oldValue = */ null, newValue);
_collection.Add(newValue);
}
}
///
/// Implementation of Freezable.CloneCore()
///
protected override void CloneCore(Freezable source)
{
base.CloneCore(source);
FreezableCollection sourceFreezableCollection = (FreezableCollection) source;
CloneCommon(sourceFreezableCollection, CloneCommonType.Clone);
}
///
/// Implementation of Freezable.CloneCurrentValueCore()
///
protected override void CloneCurrentValueCore(Freezable source)
{
base.CloneCurrentValueCore(source);
FreezableCollection sourceFreezableCollection = (FreezableCollection) source;
CloneCommon(sourceFreezableCollection, CloneCommonType.CloneCurrentValue);
}
///
/// Implementation of Freezable.GetAsFrozenCore()
///
protected override void GetAsFrozenCore(Freezable source)
{
base.GetAsFrozenCore(source);
FreezableCollection sourceFreezableCollection = (FreezableCollection) source;
CloneCommon(sourceFreezableCollection, CloneCommonType.GetAsFrozen);
}
///
/// Implementation of Freezable.GetCurrentValueAsFrozenCore()
///
protected override void GetCurrentValueAsFrozenCore(Freezable source)
{
base.GetCurrentValueAsFrozenCore(source);
FreezableCollection sourceFreezableCollection = (FreezableCollection) source;
CloneCommon(sourceFreezableCollection, CloneCommonType.GetCurrentValueAsFrozen);
}
///
/// Implementation of Freezable.FreezeCore .
///
protected override bool FreezeCore(bool isChecking)
{
bool canFreeze = base.FreezeCore(isChecking);
int count = _collection.Count;
for (int i = 0; i < count && canFreeze; i++)
{
T item = _collection[i];
Freezable itemAsFreezable = item as Freezable;
if (itemAsFreezable != null)
{
canFreeze &= Freezable.Freeze(itemAsFreezable, isChecking);
}
else
{
canFreeze &= (item.Dispatcher == null);
}
}
return canFreeze;
}
///
/// Disallow reentrant attempts to change this collection. E.g. a event handler
/// of the CollectionChanged event is not allowed to make changes to this collection.
///
///
/// typical usage is to wrap e.g. a OnCollectionChanged call with a using() scope:
///
/// using (BlockReentrancy())
/// {
/// CollectionChanged(this, new NotifyCollectionChangedEventArgs(action, item, index));
/// }
///
///
private IDisposable BlockReentrancy()
{
_monitor.Enter();
return _monitor;
}
/// Check and assert for reentrant attempts to change this collection.
/// raised when changing the collection
/// while another collection change is still being notified to other listeners
private void CheckReentrancy()
{
if (_monitor.Busy)
{
//
throw new InvalidOperationException("Cannot change FreezableCollection during a CollectionChanged event.");
}
}
#endregion ProtectedMethods
//-----------------------------------------------------
//
// Internal Fields
//
//-----------------------------------------------------
#region Internal Fields
internal List _collection;
internal uint _version = 0;
#endregion Internal Fields
//-----------------------------------------------------
//
// Private Fields
//
//------------------------------------------------------
#region Private Fields
private const string CountPropertyName = "Count";
// This must agree with Binding.IndexerName. It is declared separately
// here so as to avoid a dependency on PresentationFramework.dll.
private const string IndexerPropertyName = "Item[]";
private SimpleMonitor _monitor = new SimpleMonitor();
#endregion Private Fields
#region Enumerator
///
/// Enumerates the items in a TCollection
///
public struct Enumerator : IEnumerator, IEnumerator
{
#region Constructor
internal Enumerator(FreezableCollection list)
{
Debug.Assert(list != null, "list may not be null.");
_list = list;
_version = list._version;
_index = -1;
_current = default(T);
}
#endregion
#region Methods
void IDisposable.Dispose()
{
}
///
/// Advances the enumerator to the next element of the collection.
///
///
/// true if the enumerator was successfully advanced to the next element,
/// false if the enumerator has passed the end of the collection.
///
public bool MoveNext()
{
_list.ReadPreamble();
if (_version == _list._version)
{
if (_index > -2 && _index < _list._collection.Count - 1)
{
_current = _list._collection[++_index];
return true;
}
else
{
_index = -2; // -2 indicates "past the end"
return false;
}
}
else
{
throw new InvalidOperationException(SR.Get(SRID.Enumerator_CollectionChanged));
}
}
///
/// Sets the enumerator to its initial position, which is before the
/// first element in the collection.
///
public void Reset()
{
_list.ReadPreamble();
if (_version == _list._version)
{
_index = -1;
}
else
{
throw new InvalidOperationException(SR.Get(SRID.Enumerator_CollectionChanged));
}
}
#endregion
#region Properties
object IEnumerator.Current
{
get
{
return this.Current;
}
}
///
/// Current element
///
/// The behavior of IEnumerable<T>.Current is undefined
/// before the first MoveNext and after we have walked
/// off the end of the list. However, the IEnumerable.Current
/// contract requires that we throw exceptions
///
public T Current
{
get
{
if (_index > -1)
{
return _current;
}
else if (_index == -1)
{
throw new InvalidOperationException(SR.Get(SRID.Enumerator_NotStarted));
}
else
{
Debug.Assert(_index == -2, "expected -2, got " + _index + "\n");
throw new InvalidOperationException(SR.Get(SRID.Enumerator_ReachedEnd));
}
}
}
#endregion
#region Data
private T _current;
private FreezableCollection _list;
private uint _version;
private int _index;
#endregion
}
private class SimpleMonitor : IDisposable
{
public void Enter()
{
++ _busyCount;
}
public void Dispose()
{
-- _busyCount;
}
public bool Busy { get { return _busyCount > 0; } }
int _busyCount;
}
#endregion
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu

This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- RandomNumberGenerator.cs
- ErrorReporting.cs
- AnnotationResourceChangedEventArgs.cs
- TableLayoutPanelCellPosition.cs
- OrderPreservingPipeliningSpoolingTask.cs
- FormViewInsertEventArgs.cs
- XmlSchemaValidator.cs
- DataRow.cs
- PerformanceCounterPermissionEntry.cs
- DataGridToolTip.cs
- ListViewInsertEventArgs.cs
- ConfigurationSection.cs
- StringBlob.cs
- CapabilitiesAssignment.cs
- EmptyImpersonationContext.cs
- DynamicEntity.cs
- MailMessageEventArgs.cs
- CertificateManager.cs
- CompositeCollectionView.cs
- CompositeCollectionView.cs
- smtppermission.cs
- NativeMethods.cs
- Point3DAnimation.cs
- WindowsFormsDesignerOptionService.cs
- __ConsoleStream.cs
- ModuleBuilderData.cs
- DATA_BLOB.cs
- UInt16Converter.cs
- DispatchChannelSink.cs
- DeferredTextReference.cs
- HeaderCollection.cs
- LinqTreeNodeEvaluator.cs
- HttpWebResponse.cs
- MetadataUtilsSmi.cs
- WebRequestModuleElement.cs
- BigInt.cs
- AttachedAnnotationChangedEventArgs.cs
- DrawingGroup.cs
- AuthenticateEventArgs.cs
- HwndSourceParameters.cs
- MemoryStream.cs
- _DigestClient.cs
- WorkflowElementDialog.cs
- FontStretch.cs
- RealizationDrawingContextWalker.cs
- TabControlAutomationPeer.cs
- StringArrayEditor.cs
- Matrix3D.cs
- SapiGrammar.cs
- WebPartHelpVerb.cs
- WindowShowOrOpenTracker.cs
- XPathMultyIterator.cs
- SessionStateSection.cs
- DataListGeneralPage.cs
- LinearGradientBrush.cs
- SizeChangedInfo.cs
- ResourceReferenceExpression.cs
- AddInBase.cs
- JournalEntry.cs
- CompatibleComparer.cs
- WpfGeneratedKnownTypes.cs
- MeasureData.cs
- MessageBodyMemberAttribute.cs
- TriggerCollection.cs
- MembershipAdapter.cs
- CompilerTypeWithParams.cs
- TaiwanCalendar.cs
- KnownAssemblyEntry.cs
- _ConnectStream.cs
- ContextMarshalException.cs
- AppearanceEditorPart.cs
- QueryStringParameter.cs
- Oid.cs
- DataBindingHandlerAttribute.cs
- CodeExporter.cs
- ObjectSet.cs
- RangeValuePatternIdentifiers.cs
- TreeViewCancelEvent.cs
- Pkcs7Signer.cs
- LayoutTable.cs
- PageStatePersister.cs
- StringValidatorAttribute.cs
- ExtensionFile.cs
- Cursor.cs
- SoapIgnoreAttribute.cs
- CommandBindingCollection.cs
- ListContractAdapter.cs
- WebReferenceCollection.cs
- LinkDescriptor.cs
- TreeChangeInfo.cs
- XmlQueryRuntime.cs
- DictionaryMarkupSerializer.cs
- LoginName.cs
- OleDbFactory.cs
- Triangle.cs
- PersonalizationProviderCollection.cs
- SerializationObjectManager.cs
- Panel.cs
- XamlBrushSerializer.cs
- ModelEditingScope.cs