| File: MS\Internal\Annotations\AnnotationObservableCollection.cs | Web Access |
| Project: src\wpf\src\Microsoft.DotNet.Wpf\src\PresentationFramework\PresentationFramework.csproj (PresentationFramework) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // // // // Description: Subclass of ObservableCollection<T> which also registers for // INotifyPropertyChanged on each of its items (if T implements // INotifyPropertyChanged) and passes on the events via the // ItemChanged event. // using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; using System.Windows; namespace MS.Internal.Annotations { // An internal extension of INotifyPropertyChanged introduced in order to keep // our use of it internal and not publicly expose this interface on our OM. internal interface INotifyPropertyChanged2 : INotifyPropertyChanged { } /// <summary> /// </summary> internal class AnnotationObservableCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged2, IOwnedObject { //------------------------------------------------------ // // Constructors // //------------------------------------------------------ #region Constructors /// <summary> /// Initializes a new instance of AnnotationObservableCollection that is empty and has default initial capacity. /// </summary> public AnnotationObservableCollection() : base() { _listener = new PropertyChangedEventHandler(OnItemPropertyChanged); } /// <summary> /// Initializes a new instance of the AnnotationObservableCollection class /// that contains elements copied from the specified list /// </summary> /// <param name="list">The list whose elements are copied to the new list.</param> /// <remarks> /// The elements are copied onto the AnnotationObservableCollection in the /// same order they are read by the enumerator of the list. /// </remarks> /// <exception cref="ArgumentNullException"> list is a null reference </exception> public AnnotationObservableCollection(List<T> list) : base(list) { _listener = new PropertyChangedEventHandler(OnItemPropertyChanged); } #endregion Constructors //------------------------------------------------------ // // Public Events // //------------------------------------------------------ //------------------------------------------------------ // // Protected Methods // //------------------------------------------------------ #region Protected Methods /// <summary> /// called by base class Collection<T> when the list is being cleared; /// raises a CollectionChanged event to any listeners /// </summary> protected override void ClearItems() { foreach (INotifyPropertyChanged2 item in this) { SetOwned(item, false); } ProtectedClearItems(); } /// <summary> /// called by base class Collection<T> when an item is removed from list; /// raises a CollectionChanged event to any listeners /// </summary> protected override void RemoveItem(int index) { T removedItem = this[index]; SetOwned(removedItem, false); base.RemoveItem(index); } /// <summary> /// called by base class Collection<T> when an item is added to list; /// raises a CollectionChanged event to any listeners /// </summary> protected override void InsertItem(int index, T item) { if (ItemOwned(item)) throw new ArgumentException(SR.AlreadyHasParent); base.InsertItem(index, item); SetOwned(item, true); } /// <summary> /// called by base class Collection<T> when an item is added to list; /// raises a CollectionChanged event to any listeners /// </summary> protected override void SetItem(int index, T item) { if (ItemOwned(item)) throw new ArgumentException(SR.AlreadyHasParent); T originalItem = this[index]; SetOwned(originalItem, false); ProtectedSetItem(index, item); SetOwned(item, true); } /// <summary> /// Virtual methods allowing subclasses to change the eventing /// behavior for the ClearItems method. The default behavior /// is to call ObservableCollection's method. /// </summary> protected virtual void ProtectedClearItems() { // Use the standard built-in event base.ClearItems(); } /// <summary> /// Virtual methods allowing subclasses to change the eventing /// behavior for the SetItem method. The default behavior /// is to call Collection's defaut method and fire a single /// event. /// </summary> /// <param name="index">index of the item being set</param> /// <param name="item">item to set at that index</param> protected virtual void ProtectedSetItem(int index, T item) { // We only want to fire one event here - this collection contains items that // are within a Resource so an assignment means one resource change, period. Items[index] = item; // directly set Collection<T> inner Items collection OnPropertyChanged(new PropertyChangedEventArgs(CountString)); OnPropertyChanged(new PropertyChangedEventArgs(IndexerName)); OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } /// <summary> /// Allows subclasses to call superclass's SetItem method without /// any additional functionality added by this class. /// </summary> /// <param name="index">index of item being set</param> /// <param name="item">item to set in that index</param> protected void ObservableCollectionSetItem(int index, T item) { base.SetItem(index, item); } // raise CollectionChanged event to any listeners /// <summary> /// When an item we contain fires a PropertyChanged event we fire /// a collection changed event letting listeners know the collection /// has changed in some way. We don't care about the particulars of /// the event - just want to pass up the chain of objects that something /// has changed. /// </summary> /// <param name="sender">the object whose property changed</param> /// <param name="e">the event args describing the property that changed</param> protected virtual void OnItemPropertyChanged(object sender, PropertyChangedEventArgs e) { OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } #endregion Protected Methods //------------------------------------------------------ // // Private Methods // //------------------------------------------------------ #region Private Methods // returns whether this item already belongs to a parent object private bool ItemOwned(Object item) { if (item != null) { IOwnedObject obj = item as IOwnedObject; return obj.Owned; } return false; } // sets whether this object belongs to a parent object private void SetOwned(Object item, bool owned) { if (item != null) { IOwnedObject obj = item as IOwnedObject; obj.Owned = owned; if (owned) { ((INotifyPropertyChanged2)item).PropertyChanged += _listener; } else { ((INotifyPropertyChanged2)item).PropertyChanged -= _listener; } } } #endregion Private Methods //------------------------------------------------------ // // Private Fields // //------------------------------------------------------ #region Private & Internal Fields private readonly PropertyChangedEventHandler _listener = null; internal readonly string CountString = "Count"; internal readonly string IndexerName = "Item[]"; #endregion Private & Internal Fields } }