Code:
/ Net / Net / 3.5.50727.3053 / DEVDIV / depot / DevDiv / releases / Orcas / SP / wpf / src / Framework / MS / Internal / Data / CompositeCollectionView.cs / 1 / CompositeCollectionView.cs
//---------------------------------------------------------------------------- // //// Copyright (C) 2003 by Microsoft Corporation. All rights reserved. // // // // Description: CompositeCollectionView provides the flattened view of an CompositeCollection. // // See specs at http://avalon/connecteddata/Specs/ItemsControl.mht // http://avalon/connecteddata/Specs/CollectionView.mht // // History: // 07/14/2003 : [....] - Created // 10/08/2004 : kenlai - Refactored into only CompositeCollectionView // //--------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.Windows; using System.Windows.Data; using MS.Internal; // Invariant.Assert using MS.Internal.Controls; using System.Windows.Controls; using MS.Internal.Utility; using MS.Utility; using MS.Internal.Hashing.PresentationFramework; // HashHelper #pragma warning disable 1634, 1691 // suppressing PreSharp warnings namespace MS.Internal.Data { ////// CompositeCollectionView provides the flattened view of an CompositeCollection. /// internal sealed class CompositeCollectionView : CollectionView { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- // create the new CompositeCollectionView for a CompositeCollection internal CompositeCollectionView(CompositeCollection collection) : base(collection, -1 /* don't move to first */) // base.ctor also subscribes to CollectionChanged event of CompositeCollection { _collection = collection; _collection.ContainedCollectionChanged += new NotifyCollectionChangedEventHandler(OnContainedCollectionChanged); // Do the equivalent of MoveCurrentToFirst(), without calling virtuals int currentPosition = PrivateIsEmpty ? -1 : 0; int count = PrivateIsEmpty ? 0 : 1; SetCurrent(GetItem(currentPosition, out _currentPositionX, out _currentPositionY), currentPosition, count); } //------------------------------------------------------ // // Public Properties // //----------------------------------------------------- #region Public Properties ////// Return the estimated number of records /// ////// Count includes the number of single item in the CompositeCollection /// and the counts from the collection views of any sub-collections /// contained in public override int Count { get { // _count may also be updated through CacheCount() in // FindItem(), GetItem(), and OnContainedCollectionChanged() if (_count == -1) { _count = CountDeep(_collection.Count); // no need to raise PropertyChange for cache initialization } return _count; } } ///. /// Empty collection containers do not add to the count. /// /// Returns true if the resulting (filtered) view is emtpy. /// ////// Checks with all the collection views of any sub-collections /// contained in // This is faster than calling (Count == 0) because it stops at first item found public override bool IsEmpty { get { return PrivateIsEmpty; } } private bool PrivateIsEmpty { get { if (_count < 0) // if count cache is invalid { for (int i = 0; i < _collection.Count; ++i) { CollectionContainer cc = _collection[i] as CollectionContainer; if (cc == null || cc.ViewCount != 0) // single item or non-empty sub-collection { return false; } } CacheCount(0); // now that we know it's empty, cache it! } return (_count == 0); } } ///. /// /// Return true if public override bool IsCurrentAfterLast { get { // return (IsEmpty || (_currentPositionX >= _collection.Count)); } } ///is beyond the end or the collection is empty. /// /// Return true if public override bool IsCurrentBeforeFirst { get { // return (IsEmpty || (_currentPositionX < 0)); } } ///is before the beginning or the collection is empty. /// /// Indicates whether or not this ICollectionView can do any filtering. /// When false, set public override bool CanFilter { get { return false; } } #endregion Public Properties //------------------------------------------------------ // // Public Methods // //------------------------------------------------------ #region Public Methods ///will throw an exception. /// /// Return true if the item belongs to this view. No assumptions are /// made about the item. This method will behave similarly to IList.Contains() /// and will do an exhaustive search through all items in the flattened view. /// public override bool Contains(object item) { return (FindItem(item, false) >= 0); } ////// Return the index where the given item belongs /// /// data item public override int IndexOf(object item) { return FindItem(item, false); } ////// Retrieve item at the given zero-based index in this CollectionView. /// ////// ///The index is evaluated with any SortDescriptions or Filter being set on this CollectionView.
////// Thrown if index is out of range /// public override object GetItemAt(int index) { if (index < 0) throw new ArgumentOutOfRangeException("index"); int positionX, positionY; object item = GetItem(index, out positionX, out positionY); if (item == s_afterLast) { // couldn't find item at index item = null; throw new ArgumentOutOfRangeException("index"); } else { return item; } } ////// Move /// Move Current to this item. ///to the given item. /// If the item is not found, move to BeforeFirst. /// true if public override bool MoveCurrentTo(object item) { // if already on item, don't do anything if (Object.Equals(CurrentItem, item)) { // also check that we're not fooled by a false null CurrentItem if (item != null || IsCurrentInView) return IsCurrentInView; } if (!IsEmpty) // when empty, don't bother looking, and currency stays at BeforeFirst. FindItem(item, true); return IsCurrentInView; } ///points to an item within the view. /// Move ///to the first item. /// true if public override bool MoveCurrentToFirst() { if (IsEmpty) return false; return _MoveTo(0); } ///points to an item within the view. /// Move ///to the last item. /// true if public override bool MoveCurrentToLast() { bool oldIsCurrentAfterLast = IsCurrentAfterLast; bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst; int newPositionX, newPositionY; int lastPosition = Count - 1; object lastItem = GetLastItem(out newPositionX, out newPositionY); // searches backwards if (((CurrentPosition != lastPosition) || (CurrentItem != lastItem)) && OKToChangeCurrent()) { _currentPositionX = newPositionX; _currentPositionY = newPositionY; SetCurrent(lastItem, lastPosition); OnCurrentChanged(); if (IsCurrentAfterLast != oldIsCurrentAfterLast) OnPropertyChanged(IsCurrentAfterLastPropertyName); if (IsCurrentBeforeFirst != oldIsCurrentBeforeFirst) OnPropertyChanged(IsCurrentBeforeFirstPropertyName); OnPropertyChanged(CurrentPositionPropertyName); OnPropertyChanged(CurrentItemPropertyName); } return IsCurrentInView; } ///points to an item within the view. /// Move ///to the next item. /// true if public override bool MoveCurrentToNext() { if (IsCurrentAfterLast) return false; return _MoveTo(CurrentPosition + 1); } ///points to an item within the view. /// Move ///to the previous item. /// true if public override bool MoveCurrentToPrevious() { if (IsCurrentBeforeFirst) return false; return _MoveTo(CurrentPosition - 1); } ///points to an item within the view. /// Move /// Move CurrentItem to this index ///to the item at the given index. /// true if ///points to an item within the view. /// position is less than Before-First (-1) or greater than After-Last (Count) /// public override bool MoveCurrentToPosition(int position) { if (position < -1) throw new ArgumentOutOfRangeException("position"); int newPositionX, newPositionY; object item = GetItem(position, out newPositionX, out newPositionY); if (position != CurrentPosition || item != CurrentItem) { if (item == s_afterLast) { item = null; // check upper-bound only after GetItem() to avoid unnecessary pre-counting if (position > Count) { throw new ArgumentOutOfRangeException("position"); } } if (OKToChangeCurrent()) { bool oldIsCurrentAfterLast = IsCurrentAfterLast; bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst; _currentPositionX = newPositionX; _currentPositionY = newPositionY; SetCurrent(item, position); OnCurrentChanged(); if (IsCurrentAfterLast != oldIsCurrentAfterLast) OnPropertyChanged(IsCurrentAfterLastPropertyName); if (IsCurrentBeforeFirst != oldIsCurrentBeforeFirst) OnPropertyChanged(IsCurrentBeforeFirstPropertyName); OnPropertyChanged(CurrentPositionPropertyName); OnPropertyChanged(CurrentItemPropertyName); } } return IsCurrentInView; } ////// Re-create the view over the associated CompositeCollection /// ////// Since CompositeCollectionView does not support sorting and filtering, /// this will simply raise a Reset event to protected override void RefreshOverride() { ++_version; // tell listeners everything has changed OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } #endregion Public Methods //----------------------------------------------------- // // Protected Methods // //------------------------------------------------------ #region Protected Methods ///listeners. /// /// Implementation of IEnumerable.GetEnumerator(). /// This provides a way to enumerate the members of the collection /// without changing the currency. /// protected override IEnumerator GetEnumerator() { return new FlatteningEnumerator(_collection, this); } ////// Handle CollectionChange events from CompositeCollection ("ground level"). /// i.e. Add/Remove of single items or CollectionContainers, or Refresh /// protected override void ProcessCollectionChanged(NotifyCollectionChangedEventArgs args) { ValidateCollectionChangedEventArgs(args); bool moveCurrencyOffDeletedElement = false; switch (args.Action) { case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Remove: { // get the affected item (it can be a single first-level item or a CollectionContainer) object item = null; int startingIndex = -1; if (args.Action == NotifyCollectionChangedAction.Add) { item = args.NewItems[0]; startingIndex = args.NewStartingIndex; } else { item = args.OldItems[0]; startingIndex = args.OldStartingIndex; } Debug.Assert(startingIndex >= 0, "Source composite collection failed to supply an index"); int index = startingIndex; if (_traceLog != null) _traceLog.Add("ProcessCollectionChanged action = {0} item = {1}", args.Action, TraceLog.IdFor(item)); CollectionContainer cc = item as CollectionContainer; if (cc == null) // if a single item was added/removed { // translate the index into one that makes sense for the flat view for (int k = index - 1; k >= 0; --k) { cc = _collection[k] as CollectionContainer; if (cc != null) { // count members of cc's view but not the cc itself index += cc.ViewCount - 1; } } if (args.Action == NotifyCollectionChangedAction.Add) { if (_count >= 0) ++_count; UpdateCurrencyAfterAdd(index, args.NewStartingIndex, true); } else if (args.Action == NotifyCollectionChangedAction.Remove) { if (_count >= 0) --_count; UpdateCurrencyAfterRemove(index, args.OldStartingIndex, true); } args = new NotifyCollectionChangedEventArgs(args.Action, item, index); } else // else a whole collection container was added/removed { if (args.Action == NotifyCollectionChangedAction.Add) { if (_count >= 0) _count += cc.ViewCount; } else { // We only handle Add and Remove. Make sure it's not some other action. Debug.Assert(args.Action == NotifyCollectionChangedAction.Remove); if (_count >= 0) _count -= cc.ViewCount; } if (startingIndex <= _currentPositionX) { if (args.Action == NotifyCollectionChangedAction.Add) { ++_currentPositionX; SetCurrentPositionFromXY(_currentPositionX, _currentPositionY); } else { // We only handle Add and Remove. Make sure it's not some other action. Invariant.Assert(args.Action == NotifyCollectionChangedAction.Remove); if (startingIndex == _currentPositionX) { moveCurrencyOffDeletedElement = true; } else // (args.StartingIndex < _currentPositionX) { --_currentPositionX; SetCurrentPositionFromXY(_currentPositionX, _currentPositionY); } } } // force refresh for all listeners args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); } } break; case NotifyCollectionChangedAction.Replace: { CollectionContainer newCollectionContainer = args.NewItems[0] as CollectionContainer; CollectionContainer oldCollectionContainer = args.OldItems[0] as CollectionContainer; int startingIndex = args.OldStartingIndex; if (newCollectionContainer == null && oldCollectionContainer == null) // if a single item was added/removed { // translate the index into one that makes sense for the flat view for (int k = startingIndex - 1; k >= 0; --k) { CollectionContainer cc = _collection[k] as CollectionContainer; if (cc != null) { // count members of cc's view but not the cc itself startingIndex += cc.ViewCount - 1; } } if (startingIndex == CurrentPosition) moveCurrencyOffDeletedElement = true; args = new NotifyCollectionChangedEventArgs(args.Action, args.NewItems, args.OldItems, startingIndex); } else // else a whole collection container was replaced { if (_count >= 0) { _count -= oldCollectionContainer == null ? 1 : oldCollectionContainer.ViewCount; _count += newCollectionContainer == null ? 1 : newCollectionContainer.ViewCount; } if (startingIndex < _currentPositionX) { SetCurrentPositionFromXY(_currentPositionX, _currentPositionY); } else if (startingIndex == _currentPositionX) { moveCurrencyOffDeletedElement = true; } // force refresh for all listeners args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); } } break; case NotifyCollectionChangedAction.Move: { CollectionContainer oldCollectionContainer = args.OldItems[0] as CollectionContainer; int oldStartingIndex = args.OldStartingIndex; int newStartingIndex = args.NewStartingIndex; if (oldCollectionContainer == null) // if a single item was added/removed { // no change to count for a move operation. // translate the index into one that makes sense for the flat view for (int k = oldStartingIndex - 1; k >= 0; --k) { CollectionContainer cc = _collection[k] as CollectionContainer; if (cc != null) { // count members of cc's view but not the cc itself oldStartingIndex += cc.ViewCount - 1; } } // translate the index into one that makes sense for the flat view for (int k = newStartingIndex - 1; k >= 0; --k) { CollectionContainer cc = _collection[k] as CollectionContainer; if (cc != null) { // count members of cc's view but not the cc itself newStartingIndex += cc.ViewCount - 1; } } // if the entire move happened before or after the CurrentPosition, then // there needn't be a change to currency. if (oldStartingIndex == CurrentPosition) { moveCurrencyOffDeletedElement = true; } else if (newStartingIndex <= CurrentPosition && oldStartingIndex > CurrentPosition) { UpdateCurrencyAfterAdd(newStartingIndex, args.NewStartingIndex, true); } else if (oldStartingIndex < CurrentPosition && newStartingIndex >= CurrentPosition) { UpdateCurrencyAfterRemove(oldStartingIndex, args.OldStartingIndex, true); } args = new NotifyCollectionChangedEventArgs(args.Action, args.OldItems, newStartingIndex, oldStartingIndex); } else // else a whole collection container was moved { // no change to count for a move operation. // if the entire move happened before or after the CurrentPosition, then // there needn't be a change to currency. if (oldStartingIndex == _currentPositionX) { moveCurrencyOffDeletedElement = true; } else if (newStartingIndex <= _currentPositionX && oldStartingIndex > _currentPositionX) { ++_currentPositionX; SetCurrentPositionFromXY(_currentPositionX, _currentPositionY); } else if (oldStartingIndex < _currentPositionX && newStartingIndex >= _currentPositionX) { --_currentPositionX; SetCurrentPositionFromXY(_currentPositionX, _currentPositionY); } // force refresh for all listeners args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); } } break; case NotifyCollectionChangedAction.Reset: { if (_traceLog != null) _traceLog.Add("ProcessCollectionChanged action = {0}", args.Action); if (_collection.Count != 0) { // // This is to verify that a Reset event is raised IFF the CompositionCollection // was cleared. To fully implement a Reset event otherwise can prove to be // quite complex. For example, you must unhook the listeners to each of the // CollectionContainers that are no longer in the collection, hook up the new // ones, and figure out how to restore the currency to the correct item in // the correct sub-collection, or to BeforeFirst or AfterLast. // throw new InvalidOperationException(SR.Get(SRID.CompositeCollectionResetOnlyOnClear)); } _count = 0; // OnCollectionChanged(arg) below will raise PropChange for Count if (_currentPositionX >= 0) // if current item was in view { OnCurrentChanging(); SetCurrentBeforeFirst(); OnCurrentChanged(); OnPropertyChanged(IsCurrentBeforeFirstPropertyName); OnPropertyChanged(CurrentPositionPropertyName); OnPropertyChanged(CurrentItemPropertyName); } } break; default: throw new NotSupportedException(SR.Get(SRID.UnexpectedCollectionChangeAction, args.Action)); } ++_version; OnCollectionChanged(args); if (moveCurrencyOffDeletedElement) { _currentPositionY = 0; MoveCurrencyOffDeletedElement(); } #if DEBUG VerifyCurrencyIsConsistent(); #endif } #endregion Protected Methods //----------------------------------------------------- // // Internal Methods // //----------------------------------------------------- #region Internal Methods // collection changed event as received from the container (VIEW) of a sub-collection internal void OnContainedCollectionChanged(object sender, NotifyCollectionChangedEventArgs args) { ValidateCollectionChangedEventArgs(args); // Count must be invalidated instead of updated, because there might be // several change events raised from the same sub-collection change // (i.e. when the underlying collection is the basis of multiple collContainers.) _count = -1; int flatOldIndex = args.OldStartingIndex; int flatNewIndex = args.NewStartingIndex; // translate the index into one that makes sense for the flat view int x; int indexModifier = 0; for (x = 0; x < _collection.Count; ++x) { CollectionContainer cc = _collection[x] as CollectionContainer; if (cc != null) { if (sender == cc) { break; } indexModifier += cc.ViewCount; } else // single item { ++indexModifier; } } // if we didn't know the index to start with, we still don't if (args.OldStartingIndex >= 0) flatOldIndex += indexModifier; if (args.NewStartingIndex >= 0) flatNewIndex += indexModifier; if (x >= _collection.Count) { if (_traceLog != null) { _traceLog.Add("Received ContainerCollectionChange from unknown sender {0} action = {1} old item = {2}, new item = {3}", TraceLog.IdFor(sender), args.Action, TraceLog.IdFor(args.OldItems[0]), TraceLog.IdFor(args.NewItems[0])); _traceLog.Add("Unhook CollectionChanged event handler from unknown sender."); } // Bonus: since we've spent the time looking through the whole list, // cache the count if we didn't have it! CacheCount(indexModifier); return; } switch (args.Action) { case NotifyCollectionChangedAction.Add: TraceContainerCollectionChange(sender, args.Action, null, args.NewItems[0]); UpdateCurrencyAfterAdd(flatNewIndex, x, false); args = new NotifyCollectionChangedEventArgs(args.Action, args.NewItems[0]); break; case NotifyCollectionChangedAction.Remove: TraceContainerCollectionChange(sender, args.Action, args.OldItems[0], null); UpdateCurrencyAfterRemove(flatOldIndex, x, false); args = new NotifyCollectionChangedEventArgs(args.Action, args.OldItems[0]); break; case NotifyCollectionChangedAction.Replace: TraceContainerCollectionChange(sender, args.Action, args.OldItems[0], args.NewItems[0]); if (flatOldIndex == CurrentPosition) MoveCurrencyOffDeletedElement(); args = new NotifyCollectionChangedEventArgs(args.Action, args.NewItems[0], args.OldItems[0], flatOldIndex); break; case NotifyCollectionChangedAction.Move: TraceContainerCollectionChange(sender, args.Action, args.OldItems[0], args.NewItems[0]); UpdateCurrencyAfterMove(flatOldIndex, flatNewIndex, x, false); args = new NotifyCollectionChangedEventArgs(args.Action, args.OldItems[0], flatNewIndex, flatOldIndex); break; case NotifyCollectionChangedAction.Reset: { if (_traceLog != null) _traceLog.Add("ContainerCollectionChange from {0} action = {1}", TraceLog.IdFor(sender), args.Action); UpdateCurrencyAfterRefresh(sender); } break; default: throw new NotSupportedException(SR.Get(SRID.UnexpectedCollectionChangeAction, args.Action)); } ++_version; OnCollectionChanged(args); } // determine whether the items have reliable hash codes internal override bool HasReliableHashCodes() { // sample an item from each contained collection (bug 1738297) for (int k=0, n=_collection.Count; k= 0) { index += positionY; break; } positionY = 0; index += cc.ViewCount; // flattened index } } if (positionX >= _collection.Count) { // Bonus: since we've spent the time looking through the whole list, // cache the count if we didn't have it! CacheCount(index); index = -1; // if caller wanted to changeCurrent, we'll move to BeforeFirst item = null; positionX = -1; positionY = 0; } if (changeCurrent) { if ((CurrentPosition != index) && OKToChangeCurrent()) { object oldCurrentItem = CurrentItem; int oldCurrentPosition = CurrentPosition; bool oldIsCurrentAfterLast = IsCurrentAfterLast; bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst; SetCurrent(item, index); _currentPositionX = positionX; _currentPositionY = positionY; OnCurrentChanged(); if (IsCurrentAfterLast != oldIsCurrentAfterLast) OnPropertyChanged(IsCurrentAfterLastPropertyName); if (IsCurrentBeforeFirst != oldIsCurrentBeforeFirst) OnPropertyChanged(IsCurrentBeforeFirstPropertyName); if (oldCurrentPosition != CurrentPosition) OnPropertyChanged(CurrentPositionPropertyName); if (oldCurrentItem != CurrentItem) OnPropertyChanged(CurrentItemPropertyName); } } return index; } // if flatIndex is -1, null is returned // if flatIndex is greater than Count, s_afterLast is returned private object GetItem(int flatIndex, out int positionX, out int positionY) { positionY = 0; if (flatIndex == -1) { positionX = -1; return null; } if (_count >= 0 && flatIndex >= _count) { positionX = _collection.Count; return s_afterLast; } int searchIndex = 0; for (int i = 0; i < _collection.Count; ++i) { CollectionContainer cc = _collection[i] as CollectionContainer; if (cc == null) // flat item { if (searchIndex == flatIndex) { positionX = i; return _collection[i]; } ++searchIndex; } else if (cc.Collection != null) // CollContainer { // see if flatIndex falls within this collection: int localIndex = flatIndex - searchIndex; int count = cc.ViewCount; if (localIndex < count) { positionX = i; positionY = localIndex; return cc.ViewItem(localIndex); } else { // try next searchIndex += count; } } } // Bonus: since we've spent the time looking through the whole list, // cache the count if we didn't have it! CacheCount(searchIndex); positionX = _collection.Count; return s_afterLast; } // Beginning at the specified (positionX, positionY), // look for an item to set as CurrentItem. // ALWAYS set _currentPositionX and _currentPositionY. private object GetNextItemFromXY(int positionX, int positionY) { Invariant.Assert(positionY >= 0); object item = null; for (; positionX < _collection.Count; ++positionX) { CollectionContainer cc = _collection[positionX] as CollectionContainer; if (cc == null) { item = _collection[positionX]; positionY = 0; break; } else if (positionY < cc.ViewCount) { item = cc.ViewItem(positionY); break; } else { // after the initial positionX, forget the old Y value positionY = 0; } } if (positionX < _collection.Count) { _currentPositionX = positionX; _currentPositionY = positionY; } else { _currentPositionX = _collection.Count; _currentPositionY = 0; } return item; } // Count items in CompositeCollection, including those in sub-collections, from 0 to end. // If end is _collection.Count, this returns count of all items in collection. private int CountDeep(int end) { if (Invariant.Strict) Invariant.Assert(end <= _collection.Count); int count = 0; for (int i = 0; i < end; ++i) { CollectionContainer cc = _collection[i] as CollectionContainer; if (cc == null) // flat item { ++count; } else { count += cc.ViewCount; } } return count; } private void CacheCount(int count) { // count cache may be wrong if underlying collection doesn't notify; // also, don't count initial cache as a change. bool countChanged = (_count != count && _count >= 0); _count = count; if (countChanged) { OnPropertyChanged(CountPropertyName); } } // Move to a given index. // This current-changing operation can be cancelled and it should not throw exceptions. // if the proposed index is after the end, current is set to AfterLast private bool _MoveTo(int proposed) { int newPositionX, newPositionY; object newCurrentItem = GetItem(proposed, out newPositionX, out newPositionY); if (proposed != CurrentPosition || newCurrentItem != CurrentItem) { // if we know the count, proposed should be in range. Invariant.Assert(_count < 0 || proposed <= _count); if (OKToChangeCurrent()) { object oldCurrentItem = CurrentItem; int oldCurrentPosition = CurrentPosition; bool oldIsCurrentAfterLast = IsCurrentAfterLast; bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst; _currentPositionX = newPositionX; _currentPositionY = newPositionY; if (newCurrentItem == s_afterLast) { SetCurrent(null, Count); // Count has been cached from GetItem() } else { SetCurrent(newCurrentItem, proposed); } OnCurrentChanged(); if (IsCurrentAfterLast != oldIsCurrentAfterLast) OnPropertyChanged(IsCurrentAfterLastPropertyName); if (IsCurrentBeforeFirst != oldIsCurrentBeforeFirst) OnPropertyChanged(IsCurrentBeforeFirstPropertyName); if (oldCurrentPosition != CurrentPosition) OnPropertyChanged(CurrentPositionPropertyName); if (oldCurrentItem != CurrentItem) OnPropertyChanged(CurrentItemPropertyName); } } return IsCurrentInView; } // Update currency fields after a single item had been added. private void UpdateCurrencyAfterAdd(int flatIndex, int positionX, bool isCompositeItem) { if (flatIndex <= CurrentPosition) { int newCurrentPosition = CurrentPosition + 1; if (isCompositeItem) // if the add was a single item of CompositeCollection { ++_currentPositionX; } else if (positionX == _currentPositionX) // else if it was in the current sub-collection { ++_currentPositionY; } // else it was in a subcollection prior to the current collection, // in which case we don't need to adjust X-Y. // CurrentItem needs to be updated because we get notified of replace as Remove+Add // but that's not what really happened in the underlying collection SetCurrent(GetNextItemFromXY(_currentPositionX, _currentPositionY), newCurrentPosition); } #if DEBUG VerifyCurrencyIsConsistent(); #endif } // Update currency fields after a single item had been removed. private void UpdateCurrencyAfterRemove(int flatIndex, int positionX, bool isCompositeItem) { if (flatIndex < CurrentPosition) { SetCurrent(CurrentItem, CurrentPosition - 1); if (isCompositeItem) // if the remove was a single item of CompositeCollection { --_currentPositionX; } else if (positionX == _currentPositionX) // else if it was in the current sub-collection { --_currentPositionY; } // else it was in a subcollection prior to the current collection, // in which case we don't need to adjust X-Y. } else if (flatIndex == CurrentPosition) // current item was removed { MoveCurrencyOffDeletedElement(); } #if DEBUG VerifyCurrencyIsConsistent(); #endif } // fix up CurrentPosition and CurrentItem after a collection change private void UpdateCurrencyAfterMove(int oldIndex, int newIndex, int positionX, bool isCompositeItem) { // if entire move was before or after current item, then there // is nothing that needs to be done. if ((oldIndex < CurrentPosition && newIndex < CurrentPosition) || (oldIndex > CurrentPosition && newIndex > CurrentPosition)) return; if (newIndex <= CurrentPosition) UpdateCurrencyAfterAdd(newIndex, positionX, isCompositeItem); if (oldIndex <= CurrentPosition) UpdateCurrencyAfterRemove(oldIndex, positionX, isCompositeItem); } // Update currency fields after a sub-collection had been refreshed. private void UpdateCurrencyAfterRefresh(object refreshedObject) { Invariant.Assert(refreshedObject is CollectionContainer); object oldCurrentItem = CurrentItem; int oldCurrentPosition = CurrentPosition; bool oldIsCurrentAfterLast = IsCurrentAfterLast; bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst; if (IsCurrentInView && refreshedObject == _collection[_currentPositionX]) { CollectionContainer cc = refreshedObject as CollectionContainer; if (cc.ViewCount == 0) // if the collection was emptied out { // it's just as though the collection was deleted _currentPositionY = 0; // 0 is AfterLast for an empty collection MoveCurrencyOffDeletedElement(); } else // else try to restore currency to the old current item { int positionY = cc.ViewIndexOf(CurrentItem); if (positionY >= 0) { _currentPositionY = positionY; SetCurrentPositionFromXY(_currentPositionX, _currentPositionY); } else // else we give up and move to BeforeFirst { OnCurrentChanging(); SetCurrentBeforeFirst(); OnCurrentChanged(); } } } else { // Recalculate CurrentPosition if the refreshed collection was positioned before current collection for (int i = 0; i < _currentPositionX; ++i) { if (_collection[i] == refreshedObject) { SetCurrentPositionFromXY(_currentPositionX, _currentPositionY); break; } } } if (IsCurrentAfterLast != oldIsCurrentAfterLast) OnPropertyChanged(IsCurrentAfterLastPropertyName); if (IsCurrentBeforeFirst != oldIsCurrentBeforeFirst) OnPropertyChanged(IsCurrentBeforeFirstPropertyName); if (oldCurrentPosition != CurrentPosition) OnPropertyChanged(CurrentPositionPropertyName); if (oldCurrentItem != CurrentItem) OnPropertyChanged(CurrentItemPropertyName); #if DEBUG VerifyCurrencyIsConsistent(); #endif } private void MoveCurrencyOffDeletedElement() { int oldCurrentPosition = CurrentPosition; // We fire current changing, ignoring cancelation - there's no choice. OnCurrentChanging(); // find the next item to be current object newCurrentItem = GetNextItemFromXY(_currentPositionX, _currentPositionY); // if next item could not be found, go to the last item instead if (_currentPositionX >= _collection.Count) { newCurrentItem = GetLastItem(out _currentPositionX, out _currentPositionY); SetCurrent(newCurrentItem, Count - 1); } else { // recalculate position because the removed element could have been a collection SetCurrentPositionFromXY(_currentPositionX, _currentPositionY); SetCurrent(newCurrentItem, CurrentPosition); } OnCurrentChanged(); OnPropertyChanged(CountPropertyName); OnPropertyChanged(CurrentItemPropertyName); if (IsCurrentAfterLast) OnPropertyChanged(IsCurrentAfterLastPropertyName); if (IsCurrentBeforeFirst) OnPropertyChanged(IsCurrentBeforeFirstPropertyName); if (CurrentPosition != oldCurrentPosition) OnPropertyChanged(CurrentPositionPropertyName); } // note: if collection is empty, item and position returned is BeforeFirst private object GetLastItem(out int positionX, out int positionY) { object lastItem = null; positionX = -1; positionY = 0; if (_count != 0) // unknown or HasItems { // seek backwards positionX = _collection.Count - 1; for (; positionX >= 0; --positionX) { CollectionContainer cc = _collection[positionX] as CollectionContainer; if (cc == null) { lastItem = _collection[positionX]; break; } else if (cc.ViewCount > 0) { positionY = cc.ViewCount - 1; lastItem = cc.ViewItem(positionY); break; } } if (positionX < 0) // no items? remember zero count { CacheCount(0); } } return lastItem; } private void SetCurrentBeforeFirst() { _currentPositionX = -1; _currentPositionY = 0; SetCurrent(null, -1); } private void SetCurrentPositionFromXY(int x, int y) { if (IsCurrentBeforeFirst) SetCurrent(null, -1); else if (IsCurrentAfterLast) SetCurrent(null, Count); else SetCurrent(CurrentItem, CountDeep(x) + y); } // this method is here just to avoid the compiler error // error CS0649: Warning as Error: Field '..._traceLog' is never assigned to, and will always have its default value null void InitializeTraceLog() { _traceLog = new TraceLog(20); } private void TraceContainerCollectionChange(object sender, NotifyCollectionChangedAction action, object oldItem, object newItem) { if (_traceLog != null) _traceLog.Add("ContainerCollectionChange from {0} action = {1} oldItem = {2} newItem = {3}", TraceLog.IdFor(sender), action, TraceLog.IdFor(oldItem), TraceLog.IdFor(newItem)); } private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs e) { switch (e.Action) { case NotifyCollectionChangedAction.Add: if (e.NewItems.Count != 1) throw new NotSupportedException(SR.Get(SRID.RangeActionsNotSupported)); break; case NotifyCollectionChangedAction.Remove: if (e.OldItems.Count != 1) throw new NotSupportedException(SR.Get(SRID.RangeActionsNotSupported)); break; case NotifyCollectionChangedAction.Replace: if (e.NewItems.Count != 1 || e.OldItems.Count != 1) throw new NotSupportedException(SR.Get(SRID.RangeActionsNotSupported)); break; case NotifyCollectionChangedAction.Move: if (e.NewItems.Count != 1) throw new NotSupportedException(SR.Get(SRID.RangeActionsNotSupported)); if (e.NewStartingIndex < 0) throw new InvalidOperationException(SR.Get(SRID.CannotMoveToUnknownPosition)); break; case NotifyCollectionChangedAction.Reset: break; default: throw new NotSupportedException(SR.Get(SRID.UnexpectedCollectionChangeAction, e.Action)); } } /// /// Helper to raise a PropertyChanged event />). /// private void OnPropertyChanged(string propertyName) { OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); } #endregion Private Methods //------------------------------------------------------ // // Private Types // //----------------------------------------------------- #region Private Types ////// IEnumerator implementation that does flattened forward-only enumeration over CompositeCollectionView. /// private class FlatteningEnumerator : IEnumerator { internal FlatteningEnumerator(CompositeCollection collection, CompositeCollectionView view) { Invariant.Assert(collection != null && view != null); _collection = collection; _view = view; _version = view._version; Reset(); } public bool MoveNext() { CheckVersion(); bool isCurrentInView = true; while (true) { // advance within a collection container if (_containerEnumerator != null) { if (_containerEnumerator.MoveNext()) { _current = _containerEnumerator.Current; break; } // when we reach the end of a container, prepare to move on _containerEnumerator = null; } // move to the next item if (++_index < _collection.Count) { object item = _collection[_index]; CollectionContainer cc = item as CollectionContainer; // item is a container, move into it if (cc != null) { IEnumerable ie = cc.View; // View is null when Collection is null _containerEnumerator = (ie != null) ? ie.GetEnumerator() : null; continue; } // plain item _current = item; break; } else { // no more items _current = null; _done = true; isCurrentInView = false; break; } } return isCurrentInView; } public object Current { get { // the spec for IEnumerator.Current says: // InvalidOperationException: The enumerator is positioned before the first element of the collection or after the last element. if (_index < 0) { #pragma warning suppress 6503 // IEnumerator.Current is documented to throw this exception throw new InvalidOperationException(SR.Get(SRID.EnumeratorNotStarted)); } if (_done) { #pragma warning suppress 6503 // IEnumerator.Current is documented to throw this exception throw new InvalidOperationException(SR.Get(SRID.EnumeratorReachedEnd)); } return _current; } } public void Reset() { CheckVersion(); _index = -1; _current = null; _containerEnumerator = null; _done = false; } private void CheckVersion() { // note: there's a very unlikely possibility that the // version number wraps around back to the same number if (_isInvalidated || (_isInvalidated = (_version != _view._version))) throw new InvalidOperationException(SR.Get(SRID.EnumeratorVersionChanged)); } private CompositeCollection _collection; private CompositeCollectionView _view; private int _index; private object _current; private IEnumerator _containerEnumerator; private bool _done; private bool _isInvalidated = false; private int _version; } #endregion Private Types //------------------------------------------------------ // // Private Fields // //----------------------------------------------------- #region Private Fields TraceLog _traceLog; CompositeCollection _collection; int _count = -1; int _version = 0; // Using X-Y coordinates to track current position in the composite collection: // X is the index in the first-level collection, whose members are items and subcollections // Y is the index into the subcollection, if any. 0, if not. int _currentPositionX = -1; int _currentPositionY = 0; private static readonly object s_afterLast = new Object(); #endregion Private Fields //----------------------------------------------------- // // Debugging Aids // //----------------------------------------------------- #region Debugging Aids #if DEBUG // Verify that Currency is consistent. // However, we have to make an exception for cases when a collection is // being used multiple times inside a CompositeCollection. private void VerifyCurrencyIsConsistent() { if (IsCurrentInView) { int x, y; if ((CurrentItem != GetItem(CurrentPosition, out x, out y)) && !_collection.HasRepeatedCollection()) Debug.Assert(false, "CurrentItem is not consistent with CurrentPosition"); } else { if ((CurrentItem != null) && !_collection.HasRepeatedCollection()) Debug.Assert(false, "CurrentItem is not consistent with CurrentPosition"); } } #endif #endregion Debugging Aids } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved. //---------------------------------------------------------------------------- // //// Copyright (C) 2003 by Microsoft Corporation. All rights reserved. // // // // Description: CompositeCollectionView provides the flattened view of an CompositeCollection. // // See specs at http://avalon/connecteddata/Specs/ItemsControl.mht // http://avalon/connecteddata/Specs/CollectionView.mht // // History: // 07/14/2003 : [....] - Created // 10/08/2004 : kenlai - Refactored into only CompositeCollectionView // //--------------------------------------------------------------------------- using System; using System.Collections; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.Windows; using System.Windows.Data; using MS.Internal; // Invariant.Assert using MS.Internal.Controls; using System.Windows.Controls; using MS.Internal.Utility; using MS.Utility; using MS.Internal.Hashing.PresentationFramework; // HashHelper #pragma warning disable 1634, 1691 // suppressing PreSharp warnings namespace MS.Internal.Data { ////// CompositeCollectionView provides the flattened view of an CompositeCollection. /// internal sealed class CompositeCollectionView : CollectionView { //----------------------------------------------------- // // Constructors // //----------------------------------------------------- // create the new CompositeCollectionView for a CompositeCollection internal CompositeCollectionView(CompositeCollection collection) : base(collection, -1 /* don't move to first */) // base.ctor also subscribes to CollectionChanged event of CompositeCollection { _collection = collection; _collection.ContainedCollectionChanged += new NotifyCollectionChangedEventHandler(OnContainedCollectionChanged); // Do the equivalent of MoveCurrentToFirst(), without calling virtuals int currentPosition = PrivateIsEmpty ? -1 : 0; int count = PrivateIsEmpty ? 0 : 1; SetCurrent(GetItem(currentPosition, out _currentPositionX, out _currentPositionY), currentPosition, count); } //------------------------------------------------------ // // Public Properties // //----------------------------------------------------- #region Public Properties ////// Return the estimated number of records /// ////// Count includes the number of single item in the CompositeCollection /// and the counts from the collection views of any sub-collections /// contained in public override int Count { get { // _count may also be updated through CacheCount() in // FindItem(), GetItem(), and OnContainedCollectionChanged() if (_count == -1) { _count = CountDeep(_collection.Count); // no need to raise PropertyChange for cache initialization } return _count; } } ///. /// Empty collection containers do not add to the count. /// /// Returns true if the resulting (filtered) view is emtpy. /// ////// Checks with all the collection views of any sub-collections /// contained in // This is faster than calling (Count == 0) because it stops at first item found public override bool IsEmpty { get { return PrivateIsEmpty; } } private bool PrivateIsEmpty { get { if (_count < 0) // if count cache is invalid { for (int i = 0; i < _collection.Count; ++i) { CollectionContainer cc = _collection[i] as CollectionContainer; if (cc == null || cc.ViewCount != 0) // single item or non-empty sub-collection { return false; } } CacheCount(0); // now that we know it's empty, cache it! } return (_count == 0); } } ///. /// /// Return true if public override bool IsCurrentAfterLast { get { // return (IsEmpty || (_currentPositionX >= _collection.Count)); } } ///is beyond the end or the collection is empty. /// /// Return true if public override bool IsCurrentBeforeFirst { get { // return (IsEmpty || (_currentPositionX < 0)); } } ///is before the beginning or the collection is empty. /// /// Indicates whether or not this ICollectionView can do any filtering. /// When false, set public override bool CanFilter { get { return false; } } #endregion Public Properties //------------------------------------------------------ // // Public Methods // //------------------------------------------------------ #region Public Methods ///will throw an exception. /// /// Return true if the item belongs to this view. No assumptions are /// made about the item. This method will behave similarly to IList.Contains() /// and will do an exhaustive search through all items in the flattened view. /// public override bool Contains(object item) { return (FindItem(item, false) >= 0); } ////// Return the index where the given item belongs /// /// data item public override int IndexOf(object item) { return FindItem(item, false); } ////// Retrieve item at the given zero-based index in this CollectionView. /// ////// ///The index is evaluated with any SortDescriptions or Filter being set on this CollectionView.
////// Thrown if index is out of range /// public override object GetItemAt(int index) { if (index < 0) throw new ArgumentOutOfRangeException("index"); int positionX, positionY; object item = GetItem(index, out positionX, out positionY); if (item == s_afterLast) { // couldn't find item at index item = null; throw new ArgumentOutOfRangeException("index"); } else { return item; } } ////// Move /// Move Current to this item. ///to the given item. /// If the item is not found, move to BeforeFirst. /// true if public override bool MoveCurrentTo(object item) { // if already on item, don't do anything if (Object.Equals(CurrentItem, item)) { // also check that we're not fooled by a false null CurrentItem if (item != null || IsCurrentInView) return IsCurrentInView; } if (!IsEmpty) // when empty, don't bother looking, and currency stays at BeforeFirst. FindItem(item, true); return IsCurrentInView; } ///points to an item within the view. /// Move ///to the first item. /// true if public override bool MoveCurrentToFirst() { if (IsEmpty) return false; return _MoveTo(0); } ///points to an item within the view. /// Move ///to the last item. /// true if public override bool MoveCurrentToLast() { bool oldIsCurrentAfterLast = IsCurrentAfterLast; bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst; int newPositionX, newPositionY; int lastPosition = Count - 1; object lastItem = GetLastItem(out newPositionX, out newPositionY); // searches backwards if (((CurrentPosition != lastPosition) || (CurrentItem != lastItem)) && OKToChangeCurrent()) { _currentPositionX = newPositionX; _currentPositionY = newPositionY; SetCurrent(lastItem, lastPosition); OnCurrentChanged(); if (IsCurrentAfterLast != oldIsCurrentAfterLast) OnPropertyChanged(IsCurrentAfterLastPropertyName); if (IsCurrentBeforeFirst != oldIsCurrentBeforeFirst) OnPropertyChanged(IsCurrentBeforeFirstPropertyName); OnPropertyChanged(CurrentPositionPropertyName); OnPropertyChanged(CurrentItemPropertyName); } return IsCurrentInView; } ///points to an item within the view. /// Move ///to the next item. /// true if public override bool MoveCurrentToNext() { if (IsCurrentAfterLast) return false; return _MoveTo(CurrentPosition + 1); } ///points to an item within the view. /// Move ///to the previous item. /// true if public override bool MoveCurrentToPrevious() { if (IsCurrentBeforeFirst) return false; return _MoveTo(CurrentPosition - 1); } ///points to an item within the view. /// Move /// Move CurrentItem to this index ///to the item at the given index. /// true if ///points to an item within the view. /// position is less than Before-First (-1) or greater than After-Last (Count) /// public override bool MoveCurrentToPosition(int position) { if (position < -1) throw new ArgumentOutOfRangeException("position"); int newPositionX, newPositionY; object item = GetItem(position, out newPositionX, out newPositionY); if (position != CurrentPosition || item != CurrentItem) { if (item == s_afterLast) { item = null; // check upper-bound only after GetItem() to avoid unnecessary pre-counting if (position > Count) { throw new ArgumentOutOfRangeException("position"); } } if (OKToChangeCurrent()) { bool oldIsCurrentAfterLast = IsCurrentAfterLast; bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst; _currentPositionX = newPositionX; _currentPositionY = newPositionY; SetCurrent(item, position); OnCurrentChanged(); if (IsCurrentAfterLast != oldIsCurrentAfterLast) OnPropertyChanged(IsCurrentAfterLastPropertyName); if (IsCurrentBeforeFirst != oldIsCurrentBeforeFirst) OnPropertyChanged(IsCurrentBeforeFirstPropertyName); OnPropertyChanged(CurrentPositionPropertyName); OnPropertyChanged(CurrentItemPropertyName); } } return IsCurrentInView; } ////// Re-create the view over the associated CompositeCollection /// ////// Since CompositeCollectionView does not support sorting and filtering, /// this will simply raise a Reset event to protected override void RefreshOverride() { ++_version; // tell listeners everything has changed OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); } #endregion Public Methods //----------------------------------------------------- // // Protected Methods // //------------------------------------------------------ #region Protected Methods ///listeners. /// /// Implementation of IEnumerable.GetEnumerator(). /// This provides a way to enumerate the members of the collection /// without changing the currency. /// protected override IEnumerator GetEnumerator() { return new FlatteningEnumerator(_collection, this); } ////// Handle CollectionChange events from CompositeCollection ("ground level"). /// i.e. Add/Remove of single items or CollectionContainers, or Refresh /// protected override void ProcessCollectionChanged(NotifyCollectionChangedEventArgs args) { ValidateCollectionChangedEventArgs(args); bool moveCurrencyOffDeletedElement = false; switch (args.Action) { case NotifyCollectionChangedAction.Add: case NotifyCollectionChangedAction.Remove: { // get the affected item (it can be a single first-level item or a CollectionContainer) object item = null; int startingIndex = -1; if (args.Action == NotifyCollectionChangedAction.Add) { item = args.NewItems[0]; startingIndex = args.NewStartingIndex; } else { item = args.OldItems[0]; startingIndex = args.OldStartingIndex; } Debug.Assert(startingIndex >= 0, "Source composite collection failed to supply an index"); int index = startingIndex; if (_traceLog != null) _traceLog.Add("ProcessCollectionChanged action = {0} item = {1}", args.Action, TraceLog.IdFor(item)); CollectionContainer cc = item as CollectionContainer; if (cc == null) // if a single item was added/removed { // translate the index into one that makes sense for the flat view for (int k = index - 1; k >= 0; --k) { cc = _collection[k] as CollectionContainer; if (cc != null) { // count members of cc's view but not the cc itself index += cc.ViewCount - 1; } } if (args.Action == NotifyCollectionChangedAction.Add) { if (_count >= 0) ++_count; UpdateCurrencyAfterAdd(index, args.NewStartingIndex, true); } else if (args.Action == NotifyCollectionChangedAction.Remove) { if (_count >= 0) --_count; UpdateCurrencyAfterRemove(index, args.OldStartingIndex, true); } args = new NotifyCollectionChangedEventArgs(args.Action, item, index); } else // else a whole collection container was added/removed { if (args.Action == NotifyCollectionChangedAction.Add) { if (_count >= 0) _count += cc.ViewCount; } else { // We only handle Add and Remove. Make sure it's not some other action. Debug.Assert(args.Action == NotifyCollectionChangedAction.Remove); if (_count >= 0) _count -= cc.ViewCount; } if (startingIndex <= _currentPositionX) { if (args.Action == NotifyCollectionChangedAction.Add) { ++_currentPositionX; SetCurrentPositionFromXY(_currentPositionX, _currentPositionY); } else { // We only handle Add and Remove. Make sure it's not some other action. Invariant.Assert(args.Action == NotifyCollectionChangedAction.Remove); if (startingIndex == _currentPositionX) { moveCurrencyOffDeletedElement = true; } else // (args.StartingIndex < _currentPositionX) { --_currentPositionX; SetCurrentPositionFromXY(_currentPositionX, _currentPositionY); } } } // force refresh for all listeners args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); } } break; case NotifyCollectionChangedAction.Replace: { CollectionContainer newCollectionContainer = args.NewItems[0] as CollectionContainer; CollectionContainer oldCollectionContainer = args.OldItems[0] as CollectionContainer; int startingIndex = args.OldStartingIndex; if (newCollectionContainer == null && oldCollectionContainer == null) // if a single item was added/removed { // translate the index into one that makes sense for the flat view for (int k = startingIndex - 1; k >= 0; --k) { CollectionContainer cc = _collection[k] as CollectionContainer; if (cc != null) { // count members of cc's view but not the cc itself startingIndex += cc.ViewCount - 1; } } if (startingIndex == CurrentPosition) moveCurrencyOffDeletedElement = true; args = new NotifyCollectionChangedEventArgs(args.Action, args.NewItems, args.OldItems, startingIndex); } else // else a whole collection container was replaced { if (_count >= 0) { _count -= oldCollectionContainer == null ? 1 : oldCollectionContainer.ViewCount; _count += newCollectionContainer == null ? 1 : newCollectionContainer.ViewCount; } if (startingIndex < _currentPositionX) { SetCurrentPositionFromXY(_currentPositionX, _currentPositionY); } else if (startingIndex == _currentPositionX) { moveCurrencyOffDeletedElement = true; } // force refresh for all listeners args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); } } break; case NotifyCollectionChangedAction.Move: { CollectionContainer oldCollectionContainer = args.OldItems[0] as CollectionContainer; int oldStartingIndex = args.OldStartingIndex; int newStartingIndex = args.NewStartingIndex; if (oldCollectionContainer == null) // if a single item was added/removed { // no change to count for a move operation. // translate the index into one that makes sense for the flat view for (int k = oldStartingIndex - 1; k >= 0; --k) { CollectionContainer cc = _collection[k] as CollectionContainer; if (cc != null) { // count members of cc's view but not the cc itself oldStartingIndex += cc.ViewCount - 1; } } // translate the index into one that makes sense for the flat view for (int k = newStartingIndex - 1; k >= 0; --k) { CollectionContainer cc = _collection[k] as CollectionContainer; if (cc != null) { // count members of cc's view but not the cc itself newStartingIndex += cc.ViewCount - 1; } } // if the entire move happened before or after the CurrentPosition, then // there needn't be a change to currency. if (oldStartingIndex == CurrentPosition) { moveCurrencyOffDeletedElement = true; } else if (newStartingIndex <= CurrentPosition && oldStartingIndex > CurrentPosition) { UpdateCurrencyAfterAdd(newStartingIndex, args.NewStartingIndex, true); } else if (oldStartingIndex < CurrentPosition && newStartingIndex >= CurrentPosition) { UpdateCurrencyAfterRemove(oldStartingIndex, args.OldStartingIndex, true); } args = new NotifyCollectionChangedEventArgs(args.Action, args.OldItems, newStartingIndex, oldStartingIndex); } else // else a whole collection container was moved { // no change to count for a move operation. // if the entire move happened before or after the CurrentPosition, then // there needn't be a change to currency. if (oldStartingIndex == _currentPositionX) { moveCurrencyOffDeletedElement = true; } else if (newStartingIndex <= _currentPositionX && oldStartingIndex > _currentPositionX) { ++_currentPositionX; SetCurrentPositionFromXY(_currentPositionX, _currentPositionY); } else if (oldStartingIndex < _currentPositionX && newStartingIndex >= _currentPositionX) { --_currentPositionX; SetCurrentPositionFromXY(_currentPositionX, _currentPositionY); } // force refresh for all listeners args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset); } } break; case NotifyCollectionChangedAction.Reset: { if (_traceLog != null) _traceLog.Add("ProcessCollectionChanged action = {0}", args.Action); if (_collection.Count != 0) { // // This is to verify that a Reset event is raised IFF the CompositionCollection // was cleared. To fully implement a Reset event otherwise can prove to be // quite complex. For example, you must unhook the listeners to each of the // CollectionContainers that are no longer in the collection, hook up the new // ones, and figure out how to restore the currency to the correct item in // the correct sub-collection, or to BeforeFirst or AfterLast. // throw new InvalidOperationException(SR.Get(SRID.CompositeCollectionResetOnlyOnClear)); } _count = 0; // OnCollectionChanged(arg) below will raise PropChange for Count if (_currentPositionX >= 0) // if current item was in view { OnCurrentChanging(); SetCurrentBeforeFirst(); OnCurrentChanged(); OnPropertyChanged(IsCurrentBeforeFirstPropertyName); OnPropertyChanged(CurrentPositionPropertyName); OnPropertyChanged(CurrentItemPropertyName); } } break; default: throw new NotSupportedException(SR.Get(SRID.UnexpectedCollectionChangeAction, args.Action)); } ++_version; OnCollectionChanged(args); if (moveCurrencyOffDeletedElement) { _currentPositionY = 0; MoveCurrencyOffDeletedElement(); } #if DEBUG VerifyCurrencyIsConsistent(); #endif } #endregion Protected Methods //----------------------------------------------------- // // Internal Methods // //----------------------------------------------------- #region Internal Methods // collection changed event as received from the container (VIEW) of a sub-collection internal void OnContainedCollectionChanged(object sender, NotifyCollectionChangedEventArgs args) { ValidateCollectionChangedEventArgs(args); // Count must be invalidated instead of updated, because there might be // several change events raised from the same sub-collection change // (i.e. when the underlying collection is the basis of multiple collContainers.) _count = -1; int flatOldIndex = args.OldStartingIndex; int flatNewIndex = args.NewStartingIndex; // translate the index into one that makes sense for the flat view int x; int indexModifier = 0; for (x = 0; x < _collection.Count; ++x) { CollectionContainer cc = _collection[x] as CollectionContainer; if (cc != null) { if (sender == cc) { break; } indexModifier += cc.ViewCount; } else // single item { ++indexModifier; } } // if we didn't know the index to start with, we still don't if (args.OldStartingIndex >= 0) flatOldIndex += indexModifier; if (args.NewStartingIndex >= 0) flatNewIndex += indexModifier; if (x >= _collection.Count) { if (_traceLog != null) { _traceLog.Add("Received ContainerCollectionChange from unknown sender {0} action = {1} old item = {2}, new item = {3}", TraceLog.IdFor(sender), args.Action, TraceLog.IdFor(args.OldItems[0]), TraceLog.IdFor(args.NewItems[0])); _traceLog.Add("Unhook CollectionChanged event handler from unknown sender."); } // Bonus: since we've spent the time looking through the whole list, // cache the count if we didn't have it! CacheCount(indexModifier); return; } switch (args.Action) { case NotifyCollectionChangedAction.Add: TraceContainerCollectionChange(sender, args.Action, null, args.NewItems[0]); UpdateCurrencyAfterAdd(flatNewIndex, x, false); args = new NotifyCollectionChangedEventArgs(args.Action, args.NewItems[0]); break; case NotifyCollectionChangedAction.Remove: TraceContainerCollectionChange(sender, args.Action, args.OldItems[0], null); UpdateCurrencyAfterRemove(flatOldIndex, x, false); args = new NotifyCollectionChangedEventArgs(args.Action, args.OldItems[0]); break; case NotifyCollectionChangedAction.Replace: TraceContainerCollectionChange(sender, args.Action, args.OldItems[0], args.NewItems[0]); if (flatOldIndex == CurrentPosition) MoveCurrencyOffDeletedElement(); args = new NotifyCollectionChangedEventArgs(args.Action, args.NewItems[0], args.OldItems[0], flatOldIndex); break; case NotifyCollectionChangedAction.Move: TraceContainerCollectionChange(sender, args.Action, args.OldItems[0], args.NewItems[0]); UpdateCurrencyAfterMove(flatOldIndex, flatNewIndex, x, false); args = new NotifyCollectionChangedEventArgs(args.Action, args.OldItems[0], flatNewIndex, flatOldIndex); break; case NotifyCollectionChangedAction.Reset: { if (_traceLog != null) _traceLog.Add("ContainerCollectionChange from {0} action = {1}", TraceLog.IdFor(sender), args.Action); UpdateCurrencyAfterRefresh(sender); } break; default: throw new NotSupportedException(SR.Get(SRID.UnexpectedCollectionChangeAction, args.Action)); } ++_version; OnCollectionChanged(args); } // determine whether the items have reliable hash codes internal override bool HasReliableHashCodes() { // sample an item from each contained collection (bug 1738297) for (int k=0, n=_collection.Count; k= 0) { index += positionY; break; } positionY = 0; index += cc.ViewCount; // flattened index } } if (positionX >= _collection.Count) { // Bonus: since we've spent the time looking through the whole list, // cache the count if we didn't have it! CacheCount(index); index = -1; // if caller wanted to changeCurrent, we'll move to BeforeFirst item = null; positionX = -1; positionY = 0; } if (changeCurrent) { if ((CurrentPosition != index) && OKToChangeCurrent()) { object oldCurrentItem = CurrentItem; int oldCurrentPosition = CurrentPosition; bool oldIsCurrentAfterLast = IsCurrentAfterLast; bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst; SetCurrent(item, index); _currentPositionX = positionX; _currentPositionY = positionY; OnCurrentChanged(); if (IsCurrentAfterLast != oldIsCurrentAfterLast) OnPropertyChanged(IsCurrentAfterLastPropertyName); if (IsCurrentBeforeFirst != oldIsCurrentBeforeFirst) OnPropertyChanged(IsCurrentBeforeFirstPropertyName); if (oldCurrentPosition != CurrentPosition) OnPropertyChanged(CurrentPositionPropertyName); if (oldCurrentItem != CurrentItem) OnPropertyChanged(CurrentItemPropertyName); } } return index; } // if flatIndex is -1, null is returned // if flatIndex is greater than Count, s_afterLast is returned private object GetItem(int flatIndex, out int positionX, out int positionY) { positionY = 0; if (flatIndex == -1) { positionX = -1; return null; } if (_count >= 0 && flatIndex >= _count) { positionX = _collection.Count; return s_afterLast; } int searchIndex = 0; for (int i = 0; i < _collection.Count; ++i) { CollectionContainer cc = _collection[i] as CollectionContainer; if (cc == null) // flat item { if (searchIndex == flatIndex) { positionX = i; return _collection[i]; } ++searchIndex; } else if (cc.Collection != null) // CollContainer { // see if flatIndex falls within this collection: int localIndex = flatIndex - searchIndex; int count = cc.ViewCount; if (localIndex < count) { positionX = i; positionY = localIndex; return cc.ViewItem(localIndex); } else { // try next searchIndex += count; } } } // Bonus: since we've spent the time looking through the whole list, // cache the count if we didn't have it! CacheCount(searchIndex); positionX = _collection.Count; return s_afterLast; } // Beginning at the specified (positionX, positionY), // look for an item to set as CurrentItem. // ALWAYS set _currentPositionX and _currentPositionY. private object GetNextItemFromXY(int positionX, int positionY) { Invariant.Assert(positionY >= 0); object item = null; for (; positionX < _collection.Count; ++positionX) { CollectionContainer cc = _collection[positionX] as CollectionContainer; if (cc == null) { item = _collection[positionX]; positionY = 0; break; } else if (positionY < cc.ViewCount) { item = cc.ViewItem(positionY); break; } else { // after the initial positionX, forget the old Y value positionY = 0; } } if (positionX < _collection.Count) { _currentPositionX = positionX; _currentPositionY = positionY; } else { _currentPositionX = _collection.Count; _currentPositionY = 0; } return item; } // Count items in CompositeCollection, including those in sub-collections, from 0 to end. // If end is _collection.Count, this returns count of all items in collection. private int CountDeep(int end) { if (Invariant.Strict) Invariant.Assert(end <= _collection.Count); int count = 0; for (int i = 0; i < end; ++i) { CollectionContainer cc = _collection[i] as CollectionContainer; if (cc == null) // flat item { ++count; } else { count += cc.ViewCount; } } return count; } private void CacheCount(int count) { // count cache may be wrong if underlying collection doesn't notify; // also, don't count initial cache as a change. bool countChanged = (_count != count && _count >= 0); _count = count; if (countChanged) { OnPropertyChanged(CountPropertyName); } } // Move to a given index. // This current-changing operation can be cancelled and it should not throw exceptions. // if the proposed index is after the end, current is set to AfterLast private bool _MoveTo(int proposed) { int newPositionX, newPositionY; object newCurrentItem = GetItem(proposed, out newPositionX, out newPositionY); if (proposed != CurrentPosition || newCurrentItem != CurrentItem) { // if we know the count, proposed should be in range. Invariant.Assert(_count < 0 || proposed <= _count); if (OKToChangeCurrent()) { object oldCurrentItem = CurrentItem; int oldCurrentPosition = CurrentPosition; bool oldIsCurrentAfterLast = IsCurrentAfterLast; bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst; _currentPositionX = newPositionX; _currentPositionY = newPositionY; if (newCurrentItem == s_afterLast) { SetCurrent(null, Count); // Count has been cached from GetItem() } else { SetCurrent(newCurrentItem, proposed); } OnCurrentChanged(); if (IsCurrentAfterLast != oldIsCurrentAfterLast) OnPropertyChanged(IsCurrentAfterLastPropertyName); if (IsCurrentBeforeFirst != oldIsCurrentBeforeFirst) OnPropertyChanged(IsCurrentBeforeFirstPropertyName); if (oldCurrentPosition != CurrentPosition) OnPropertyChanged(CurrentPositionPropertyName); if (oldCurrentItem != CurrentItem) OnPropertyChanged(CurrentItemPropertyName); } } return IsCurrentInView; } // Update currency fields after a single item had been added. private void UpdateCurrencyAfterAdd(int flatIndex, int positionX, bool isCompositeItem) { if (flatIndex <= CurrentPosition) { int newCurrentPosition = CurrentPosition + 1; if (isCompositeItem) // if the add was a single item of CompositeCollection { ++_currentPositionX; } else if (positionX == _currentPositionX) // else if it was in the current sub-collection { ++_currentPositionY; } // else it was in a subcollection prior to the current collection, // in which case we don't need to adjust X-Y. // CurrentItem needs to be updated because we get notified of replace as Remove+Add // but that's not what really happened in the underlying collection SetCurrent(GetNextItemFromXY(_currentPositionX, _currentPositionY), newCurrentPosition); } #if DEBUG VerifyCurrencyIsConsistent(); #endif } // Update currency fields after a single item had been removed. private void UpdateCurrencyAfterRemove(int flatIndex, int positionX, bool isCompositeItem) { if (flatIndex < CurrentPosition) { SetCurrent(CurrentItem, CurrentPosition - 1); if (isCompositeItem) // if the remove was a single item of CompositeCollection { --_currentPositionX; } else if (positionX == _currentPositionX) // else if it was in the current sub-collection { --_currentPositionY; } // else it was in a subcollection prior to the current collection, // in which case we don't need to adjust X-Y. } else if (flatIndex == CurrentPosition) // current item was removed { MoveCurrencyOffDeletedElement(); } #if DEBUG VerifyCurrencyIsConsistent(); #endif } // fix up CurrentPosition and CurrentItem after a collection change private void UpdateCurrencyAfterMove(int oldIndex, int newIndex, int positionX, bool isCompositeItem) { // if entire move was before or after current item, then there // is nothing that needs to be done. if ((oldIndex < CurrentPosition && newIndex < CurrentPosition) || (oldIndex > CurrentPosition && newIndex > CurrentPosition)) return; if (newIndex <= CurrentPosition) UpdateCurrencyAfterAdd(newIndex, positionX, isCompositeItem); if (oldIndex <= CurrentPosition) UpdateCurrencyAfterRemove(oldIndex, positionX, isCompositeItem); } // Update currency fields after a sub-collection had been refreshed. private void UpdateCurrencyAfterRefresh(object refreshedObject) { Invariant.Assert(refreshedObject is CollectionContainer); object oldCurrentItem = CurrentItem; int oldCurrentPosition = CurrentPosition; bool oldIsCurrentAfterLast = IsCurrentAfterLast; bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst; if (IsCurrentInView && refreshedObject == _collection[_currentPositionX]) { CollectionContainer cc = refreshedObject as CollectionContainer; if (cc.ViewCount == 0) // if the collection was emptied out { // it's just as though the collection was deleted _currentPositionY = 0; // 0 is AfterLast for an empty collection MoveCurrencyOffDeletedElement(); } else // else try to restore currency to the old current item { int positionY = cc.ViewIndexOf(CurrentItem); if (positionY >= 0) { _currentPositionY = positionY; SetCurrentPositionFromXY(_currentPositionX, _currentPositionY); } else // else we give up and move to BeforeFirst { OnCurrentChanging(); SetCurrentBeforeFirst(); OnCurrentChanged(); } } } else { // Recalculate CurrentPosition if the refreshed collection was positioned before current collection for (int i = 0; i < _currentPositionX; ++i) { if (_collection[i] == refreshedObject) { SetCurrentPositionFromXY(_currentPositionX, _currentPositionY); break; } } } if (IsCurrentAfterLast != oldIsCurrentAfterLast) OnPropertyChanged(IsCurrentAfterLastPropertyName); if (IsCurrentBeforeFirst != oldIsCurrentBeforeFirst) OnPropertyChanged(IsCurrentBeforeFirstPropertyName); if (oldCurrentPosition != CurrentPosition) OnPropertyChanged(CurrentPositionPropertyName); if (oldCurrentItem != CurrentItem) OnPropertyChanged(CurrentItemPropertyName); #if DEBUG VerifyCurrencyIsConsistent(); #endif } private void MoveCurrencyOffDeletedElement() { int oldCurrentPosition = CurrentPosition; // We fire current changing, ignoring cancelation - there's no choice. OnCurrentChanging(); // find the next item to be current object newCurrentItem = GetNextItemFromXY(_currentPositionX, _currentPositionY); // if next item could not be found, go to the last item instead if (_currentPositionX >= _collection.Count) { newCurrentItem = GetLastItem(out _currentPositionX, out _currentPositionY); SetCurrent(newCurrentItem, Count - 1); } else { // recalculate position because the removed element could have been a collection SetCurrentPositionFromXY(_currentPositionX, _currentPositionY); SetCurrent(newCurrentItem, CurrentPosition); } OnCurrentChanged(); OnPropertyChanged(CountPropertyName); OnPropertyChanged(CurrentItemPropertyName); if (IsCurrentAfterLast) OnPropertyChanged(IsCurrentAfterLastPropertyName); if (IsCurrentBeforeFirst) OnPropertyChanged(IsCurrentBeforeFirstPropertyName); if (CurrentPosition != oldCurrentPosition) OnPropertyChanged(CurrentPositionPropertyName); } // note: if collection is empty, item and position returned is BeforeFirst private object GetLastItem(out int positionX, out int positionY) { object lastItem = null; positionX = -1; positionY = 0; if (_count != 0) // unknown or HasItems { // seek backwards positionX = _collection.Count - 1; for (; positionX >= 0; --positionX) { CollectionContainer cc = _collection[positionX] as CollectionContainer; if (cc == null) { lastItem = _collection[positionX]; break; } else if (cc.ViewCount > 0) { positionY = cc.ViewCount - 1; lastItem = cc.ViewItem(positionY); break; } } if (positionX < 0) // no items? remember zero count { CacheCount(0); } } return lastItem; } private void SetCurrentBeforeFirst() { _currentPositionX = -1; _currentPositionY = 0; SetCurrent(null, -1); } private void SetCurrentPositionFromXY(int x, int y) { if (IsCurrentBeforeFirst) SetCurrent(null, -1); else if (IsCurrentAfterLast) SetCurrent(null, Count); else SetCurrent(CurrentItem, CountDeep(x) + y); } // this method is here just to avoid the compiler error // error CS0649: Warning as Error: Field '..._traceLog' is never assigned to, and will always have its default value null void InitializeTraceLog() { _traceLog = new TraceLog(20); } private void TraceContainerCollectionChange(object sender, NotifyCollectionChangedAction action, object oldItem, object newItem) { if (_traceLog != null) _traceLog.Add("ContainerCollectionChange from {0} action = {1} oldItem = {2} newItem = {3}", TraceLog.IdFor(sender), action, TraceLog.IdFor(oldItem), TraceLog.IdFor(newItem)); } private void ValidateCollectionChangedEventArgs(NotifyCollectionChangedEventArgs e) { switch (e.Action) { case NotifyCollectionChangedAction.Add: if (e.NewItems.Count != 1) throw new NotSupportedException(SR.Get(SRID.RangeActionsNotSupported)); break; case NotifyCollectionChangedAction.Remove: if (e.OldItems.Count != 1) throw new NotSupportedException(SR.Get(SRID.RangeActionsNotSupported)); break; case NotifyCollectionChangedAction.Replace: if (e.NewItems.Count != 1 || e.OldItems.Count != 1) throw new NotSupportedException(SR.Get(SRID.RangeActionsNotSupported)); break; case NotifyCollectionChangedAction.Move: if (e.NewItems.Count != 1) throw new NotSupportedException(SR.Get(SRID.RangeActionsNotSupported)); if (e.NewStartingIndex < 0) throw new InvalidOperationException(SR.Get(SRID.CannotMoveToUnknownPosition)); break; case NotifyCollectionChangedAction.Reset: break; default: throw new NotSupportedException(SR.Get(SRID.UnexpectedCollectionChangeAction, e.Action)); } } /// /// Helper to raise a PropertyChanged event />). /// private void OnPropertyChanged(string propertyName) { OnPropertyChanged(new PropertyChangedEventArgs(propertyName)); } #endregion Private Methods //------------------------------------------------------ // // Private Types // //----------------------------------------------------- #region Private Types ////// IEnumerator implementation that does flattened forward-only enumeration over CompositeCollectionView. /// private class FlatteningEnumerator : IEnumerator { internal FlatteningEnumerator(CompositeCollection collection, CompositeCollectionView view) { Invariant.Assert(collection != null && view != null); _collection = collection; _view = view; _version = view._version; Reset(); } public bool MoveNext() { CheckVersion(); bool isCurrentInView = true; while (true) { // advance within a collection container if (_containerEnumerator != null) { if (_containerEnumerator.MoveNext()) { _current = _containerEnumerator.Current; break; } // when we reach the end of a container, prepare to move on _containerEnumerator = null; } // move to the next item if (++_index < _collection.Count) { object item = _collection[_index]; CollectionContainer cc = item as CollectionContainer; // item is a container, move into it if (cc != null) { IEnumerable ie = cc.View; // View is null when Collection is null _containerEnumerator = (ie != null) ? ie.GetEnumerator() : null; continue; } // plain item _current = item; break; } else { // no more items _current = null; _done = true; isCurrentInView = false; break; } } return isCurrentInView; } public object Current { get { // the spec for IEnumerator.Current says: // InvalidOperationException: The enumerator is positioned before the first element of the collection or after the last element. if (_index < 0) { #pragma warning suppress 6503 // IEnumerator.Current is documented to throw this exception throw new InvalidOperationException(SR.Get(SRID.EnumeratorNotStarted)); } if (_done) { #pragma warning suppress 6503 // IEnumerator.Current is documented to throw this exception throw new InvalidOperationException(SR.Get(SRID.EnumeratorReachedEnd)); } return _current; } } public void Reset() { CheckVersion(); _index = -1; _current = null; _containerEnumerator = null; _done = false; } private void CheckVersion() { // note: there's a very unlikely possibility that the // version number wraps around back to the same number if (_isInvalidated || (_isInvalidated = (_version != _view._version))) throw new InvalidOperationException(SR.Get(SRID.EnumeratorVersionChanged)); } private CompositeCollection _collection; private CompositeCollectionView _view; private int _index; private object _current; private IEnumerator _containerEnumerator; private bool _done; private bool _isInvalidated = false; private int _version; } #endregion Private Types //------------------------------------------------------ // // Private Fields // //----------------------------------------------------- #region Private Fields TraceLog _traceLog; CompositeCollection _collection; int _count = -1; int _version = 0; // Using X-Y coordinates to track current position in the composite collection: // X is the index in the first-level collection, whose members are items and subcollections // Y is the index into the subcollection, if any. 0, if not. int _currentPositionX = -1; int _currentPositionY = 0; private static readonly object s_afterLast = new Object(); #endregion Private Fields //----------------------------------------------------- // // Debugging Aids // //----------------------------------------------------- #region Debugging Aids #if DEBUG // Verify that Currency is consistent. // However, we have to make an exception for cases when a collection is // being used multiple times inside a CompositeCollection. private void VerifyCurrencyIsConsistent() { if (IsCurrentInView) { int x, y; if ((CurrentItem != GetItem(CurrentPosition, out x, out y)) && !_collection.HasRepeatedCollection()) Debug.Assert(false, "CurrentItem is not consistent with CurrentPosition"); } else { if ((CurrentItem != null) && !_collection.HasRepeatedCollection()) Debug.Assert(false, "CurrentItem is not consistent with CurrentPosition"); } } #endif #endregion Debugging Aids } } // 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
- SplitterPanel.cs
- ListViewInsertedEventArgs.cs
- StringConverter.cs
- elementinformation.cs
- ClientRoleProvider.cs
- SQLRoleProvider.cs
- Console.cs
- Transform.cs
- UserControlParser.cs
- _NtlmClient.cs
- DateTimeOffsetAdapter.cs
- ToolStripTemplateNode.cs
- ProfileSection.cs
- TemplateColumn.cs
- CellRelation.cs
- ArgumentsParser.cs
- CreatingCookieEventArgs.cs
- DCSafeHandle.cs
- DataGridCell.cs
- EndEvent.cs
- ClientRolePrincipal.cs
- XsdValidatingReader.cs
- WinEventWrap.cs
- CurrencyManager.cs
- SqlDataAdapter.cs
- NetCodeGroup.cs
- MatrixTransform.cs
- DefaultValueAttribute.cs
- WsatServiceCertificate.cs
- ExpressionDumper.cs
- NumberFormatInfo.cs
- XamlFilter.cs
- StylusOverProperty.cs
- MemberListBinding.cs
- IndentTextWriter.cs
- DecimalConstantAttribute.cs
- TextDecorationUnitValidation.cs
- WebUtil.cs
- DropTarget.cs
- SelectorAutomationPeer.cs
- PropertyTabAttribute.cs
- XmlSchemaSimpleTypeList.cs
- UIntPtr.cs
- DesignerTransactionCloseEvent.cs
- OleDbWrapper.cs
- OracleBFile.cs
- ListSortDescriptionCollection.cs
- HealthMonitoringSection.cs
- CompilationUnit.cs
- SizeConverter.cs
- SafeArrayTypeMismatchException.cs
- IriParsingElement.cs
- ItemCollection.cs
- Point3DAnimationBase.cs
- LoginName.cs
- InternalResources.cs
- String.cs
- UidManager.cs
- WebBrowserBase.cs
- CachingHintValidation.cs
- sqlcontext.cs
- MetadataSerializer.cs
- OperationDescriptionCollection.cs
- RadioButtonList.cs
- SqlCacheDependencyDatabaseCollection.cs
- LinkTarget.cs
- RelationshipEndCollection.cs
- ToolStripSystemRenderer.cs
- OdbcConnectionPoolProviderInfo.cs
- Lease.cs
- MenuScrollingVisibilityConverter.cs
- ListBindableAttribute.cs
- WebRequestModuleElementCollection.cs
- ThemeDirectoryCompiler.cs
- StateMachineTimers.cs
- CssClassPropertyAttribute.cs
- AxisAngleRotation3D.cs
- ControlBindingsCollection.cs
- CanonicalFontFamilyReference.cs
- HwndSubclass.cs
- AxisAngleRotation3D.cs
- CryptoStream.cs
- CellTreeNodeVisitors.cs
- Evaluator.cs
- Empty.cs
- XmlNamespaceMappingCollection.cs
- TranslateTransform3D.cs
- ClickablePoint.cs
- XmlNode.cs
- XmlSchemaSimpleContentExtension.cs
- MonitoringDescriptionAttribute.cs
- RuntimeHandles.cs
- StateWorkerRequest.cs
- ReadOnlyDictionary.cs
- XomlSerializationHelpers.cs
- Viewport3DVisual.cs
- AdvancedBindingPropertyDescriptor.cs
- AxHost.cs
- AspNetSynchronizationContext.cs
- OdbcDataAdapter.cs