Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / MS / Internal / Data / EnumerableCollectionView.cs / 1305600 / EnumerableCollectionView.cs
//----------------------------------------------------------------------------
//
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//
// Description: Collection view over an IEnumerable.
//
//---------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading;
using System.Windows;
using System.Windows.Data;
namespace MS.Internal.Data
{
///
/// Collection view over an IEnumerable.
///
internal class EnumerableCollectionView : CollectionView
{
#region Constructors
//-----------------------------------------------------
//
// Constructors
//
//-----------------------------------------------------
// Set up a ListCollectionView over the
// snapshot. We will delegate all CollectionView functionality
// to this view.
internal EnumerableCollectionView(IEnumerable source)
: base(source, -1)
{
_snapshot = new ObservableCollection();
LoadSnapshotCore(source);
if (_snapshot.Count > 0)
{
SetCurrent(_snapshot[0], 0, 1);
}
else
{
SetCurrent(null, -1, 0);
}
// if the source doesn't raise collection change events, try to
// detect changes by polling the enumerator
_pollForChanges = !(source is INotifyCollectionChanged);
_view = new ListCollectionView(_snapshot);
INotifyCollectionChanged incc = _view as INotifyCollectionChanged;
incc.CollectionChanged += new NotifyCollectionChangedEventHandler(_OnViewChanged);
INotifyPropertyChanged ipc = _view as INotifyPropertyChanged;
ipc.PropertyChanged += new PropertyChangedEventHandler(_OnPropertyChanged);
_view.CurrentChanging += new CurrentChangingEventHandler(_OnCurrentChanging);
_view.CurrentChanged += new EventHandler(_OnCurrentChanged);
}
#endregion Constructors
//------------------------------------------------------
//
// Interfaces
//
//-----------------------------------------------------
#region ICollectionView
///
/// Culture to use during sorting.
///
public override System.Globalization.CultureInfo Culture
{
get { return _view.Culture; }
set { _view.Culture = value; }
}
///
/// Return true if the item belongs to this view. No assumptions are
/// made about the item. This method will behave similarly to IList.Contains().
/// If the caller knows that the item belongs to the
/// underlying collection, it is more efficient to call PassesFilter.
///
public override bool Contains(object item)
{
EnsureSnapshot();
return _view.Contains(item);
}
///
/// Set/get a filter callback to filter out items in collection.
/// This property will always accept a filter, but the collection view for the
/// underlying InnerList or ItemsSource may not actually support filtering.
/// Please check
///
///
/// Collections assigned to ItemsSource may not support filtering and could throw a NotSupportedException.
/// Use property to test if sorting is supported before adding
/// to SortDescriptions.
///
public override Predicate Filter
{
get { return _view.Filter; }
set { _view.Filter = value; }
}
///
/// Test if this ICollectionView supports filtering before assigning
/// a filter callback to .
///
public override bool CanFilter
{
get { return _view.CanFilter; }
}
///
/// Set/get Sort criteria to sort items in collection.
///
///
///
/// Clear a sort criteria by assigning SortDescription.Empty to this property.
/// One or more sort criteria in form of
/// can be used, each specifying a property and direction to sort by.
///
///
///
/// Simpler implementations do not support sorting and will throw a NotSupportedException.
/// Use property to test if sorting is supported before adding
/// to SortDescriptions.
///
public override SortDescriptionCollection SortDescriptions
{
get { return _view.SortDescriptions; }
}
///
/// Test if this ICollectionView supports sorting before adding
/// to .
///
public override bool CanSort
{
get { return _view.CanSort; }
}
///
/// Returns true if this view really supports grouping.
/// When this returns false, the rest of the interface is ignored.
///
public override bool CanGroup
{
get { return _view.CanGroup; }
}
///
/// The description of grouping, indexed by level.
///
public override ObservableCollection GroupDescriptions
{
get { return _view.GroupDescriptions; }
}
///
/// The top-level groups, constructed according to the descriptions
/// given in GroupDescriptions.
///
public override ReadOnlyObservableCollection Groups
{
get { return _view.Groups; }
}
///
/// Enter a Defer Cycle.
/// Defer cycles are used to coalesce changes to the ICollectionView.
///
public override IDisposable DeferRefresh()
{
return _view.DeferRefresh();
}
/// Return current item.
public override object CurrentItem
{
get { return _view.CurrentItem; }
}
///
/// The ordinal position of the within the (optionally
/// sorted and filtered) view.
///
public override int CurrentPosition
{
get { return _view.CurrentPosition; }
}
/// Return true if currency is beyond the end (End-Of-File).
public override bool IsCurrentAfterLast
{
get { return _view.IsCurrentAfterLast; }
}
/// Return true if currency is before the beginning (Beginning-Of-File).
public override bool IsCurrentBeforeFirst
{
get { return _view.IsCurrentBeforeFirst; }
}
/// Move to the first item.
public override bool MoveCurrentToFirst()
{
return _view.MoveCurrentToFirst();
}
/// Move to the previous item.
public override bool MoveCurrentToPrevious()
{
return _view.MoveCurrentToPrevious();
}
/// Move to the next item.
public override bool MoveCurrentToNext()
{
return _view.MoveCurrentToNext();
}
/// Move to the last item.
public override bool MoveCurrentToLast()
{
return _view.MoveCurrentToLast();
}
/// Move to the given item.
public override bool MoveCurrentTo(object item)
{
return _view.MoveCurrentTo(item);
}
/// Move CurrentItem to this index
public override bool MoveCurrentToPosition(int position)
{
//
// If the index is out of range here, I'll let the
// _view be the one to make that determination.
//
return _view.MoveCurrentToPosition(position);
}
#endregion ICollectionView
//------------------------------------------------------
//
// Public Properties
//
//------------------------------------------------------
#region Public Properties
///
/// Return the number of records (or -1, meaning "don't know").
/// A virtualizing view should return the best estimate it can
/// without de-virtualizing all the data. A non-virtualizing view
/// should return the exact count of its (filtered) data.
///
public override int Count
{
get
{
EnsureSnapshot();
return _view.Count;
}
}
public override bool IsEmpty
{
get
{
EnsureSnapshot();
return (_view != null) ? _view.IsEmpty : true;
}
}
///
/// Returns true if this view needs to be refreshed.
///
public override bool NeedsRefresh
{
get { return _view.NeedsRefresh; }
}
#endregion Public Properties
//-----------------------------------------------------
//
// Public Methods
//
//------------------------------------------------------
#region Public Methods
/// Return the index where the given item appears, or -1 if doesn't appear.
///
/// data item
public override int IndexOf(object item)
{
EnsureSnapshot();
return _view.IndexOf(item);
}
///
/// Return true if the item belongs to this view. The item is assumed to belong to the
/// underlying DataCollection; this method merely takes filters into account.
/// It is commonly used during collection-changed notifications to determine if the added/removed
/// item requires processing.
/// Returns true if no filter is set on collection view.
///
public override bool PassesFilter(object item)
{
if (_view.CanFilter && _view.Filter != null)
return _view.Filter(item);
return true;
}
///
/// Retrieve item at the given zero-based index in this CollectionView.
///
///
/// Thrown if index is out of range
///
public override object GetItemAt(int index)
{
EnsureSnapshot();
return _view.GetItemAt(index);
}
#endregion Public Methods
//-----------------------------------------------------
//
// Protected Methods
//
//-----------------------------------------------------
#region Protected Methods
/// Implementation of IEnumerable.GetEnumerator().
/// This provides a way to enumerate the members of the collection
/// without changing the currency.
///
protected override IEnumerator GetEnumerator()
{
EnsureSnapshot();
return ((IEnumerable) _view).GetEnumerator();
}
/// Re-create the view, using any .
protected override void RefreshOverride()
{
LoadSnapshot(SourceCollection);
}
///
/// Must be implemented by the derived classes to process a single change on the
/// UI thread. The UI thread will have already been entered by now.
///
///
/// The NotifyCollectionChangedEventArgs to be processed.
///
protected override void ProcessCollectionChanged(NotifyCollectionChangedEventArgs args)
{
// ignore events received during initialization
if (_view == null)
return;
// apply the change to the snapshot
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
if (args.NewStartingIndex < 0 || _snapshot.Count <= args.NewStartingIndex)
{ // append
for (int i=0; i=0; --i)
{
_snapshot.Insert(args.NewStartingIndex, args.NewItems[i]);
}
}
break;
case NotifyCollectionChangedAction.Remove:
if (args.OldStartingIndex < 0)
throw new InvalidOperationException(SR.Get(SRID.RemovedItemNotFound));
for (int i=args.OldItems.Count-1, index=args.OldStartingIndex+i; i>=0; --i, --index)
{
if (!Object.Equals(args.OldItems[i], _snapshot[index]))
//
throw new InvalidOperationException(SR.Get(SRID.AddedItemNotAtIndex, index));
_snapshot.RemoveAt(index);
}
break;
case NotifyCollectionChangedAction.Replace:
for (int i=args.NewItems.Count-1, index=args.NewStartingIndex+i; i>=0; --i, --index)
{
if (!Object.Equals(args.OldItems[i], _snapshot[index]))
//
throw new InvalidOperationException(SR.Get(SRID.AddedItemNotAtIndex, index));
_snapshot[index] = args.NewItems[i];
}
break;
case NotifyCollectionChangedAction.Move:
if (args.NewStartingIndex < 0)
throw new InvalidOperationException(SR.Get(SRID.CannotMoveToUnknownPosition));
if (args.OldStartingIndex < args.NewStartingIndex)
{
for (int i = args.OldItems.Count - 1,
oldIndex = args.OldStartingIndex + i,
newIndex = args.NewStartingIndex + i;
i >= 0;
--i, --oldIndex, --newIndex)
{
if (!Object.Equals(args.OldItems[i], _snapshot[oldIndex]))
//
throw new InvalidOperationException(SR.Get(SRID.AddedItemNotAtIndex, oldIndex));
_snapshot.Move(oldIndex, newIndex);
}
}
else
{
for (int i = 0,
oldIndex = args.OldStartingIndex + i,
newIndex = args.NewStartingIndex + i;
i < args.OldItems.Count;
++i, ++oldIndex, ++newIndex)
{
if (!Object.Equals(args.OldItems[i], _snapshot[oldIndex]))
//
throw new InvalidOperationException(SR.Get(SRID.AddedItemNotAtIndex, oldIndex));
_snapshot.Move(oldIndex, newIndex);
}
}
break;
case NotifyCollectionChangedAction.Reset:
LoadSnapshot(SourceCollection);
break;
}
}
#endregion Protected Methods
//-----------------------------------------------------
//
// Private Methods
//
//------------------------------------------------------
#region Private Methods
// Load a snapshot of the contents of the IEnumerable into the
// ObservableCollection.
void LoadSnapshot(IEnumerable source)
{
// force currency off the collection (gives user a chance to save dirty information)
OnCurrentChanging();
// remember the values of the scalar properties, so that we can restore
// them and raise events after reloading the data
object oldCurrentItem = CurrentItem;
int oldCurrentPosition = CurrentPosition;
bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
bool oldIsCurrentAfterLast = IsCurrentAfterLast;
// reload the data
LoadSnapshotCore(source);
// tell listeners everything has changed
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
OnCurrentChanged();
if (IsCurrentAfterLast != oldIsCurrentAfterLast)
OnPropertyChanged(new PropertyChangedEventArgs(IsCurrentAfterLastPropertyName));
if (IsCurrentBeforeFirst != oldIsCurrentBeforeFirst)
OnPropertyChanged(new PropertyChangedEventArgs(IsCurrentBeforeFirstPropertyName));
if (oldCurrentPosition != CurrentPosition)
OnPropertyChanged(new PropertyChangedEventArgs(CurrentPositionPropertyName));
if (oldCurrentItem != CurrentItem)
OnPropertyChanged(new PropertyChangedEventArgs(CurrentItemPropertyName));
}
void LoadSnapshotCore(IEnumerable source)
{
_trackingEnumerator = source.GetEnumerator();
using (IgnoreViewEvents())
{
_snapshot.Clear();
while (_trackingEnumerator.MoveNext())
{
_snapshot.Add(_trackingEnumerator.Current);
}
}
}
// if the IEnumerable has changed, bring the snapshot up to date.
// (This isn't necessary if the IEnumerable is also INotifyCollectionChanged
// because we keep the snapshot in [....] incrementally.)
void EnsureSnapshot()
{
if (_pollForChanges)
{
try
{
_trackingEnumerator.MoveNext();
}
catch (InvalidOperationException)
{
//
if (TraceData.IsEnabled && !_warningHasBeenRaised)
{
_warningHasBeenRaised = true;
TraceData.Trace(TraceEventType.Warning,
TraceData.CollectionChangedWithoutNotification(SourceCollection.GetType().FullName));
}
// collection was changed - start over with a new enumerator
LoadSnapshotCore(SourceCollection);
}
}
}
IDisposable IgnoreViewEvents()
{
return new IgnoreViewEventsHelper(this);
}
void BeginIgnoreEvents()
{
++ _ignoreEventsLevel;
}
void EndIgnoreEvents()
{
-- _ignoreEventsLevel;
}
// forward events from the internal view to our own listeners
void _OnPropertyChanged(object sender, PropertyChangedEventArgs args)
{
if (_ignoreEventsLevel != 0)
return;
OnPropertyChanged(args);
}
void _OnViewChanged(object sender, NotifyCollectionChangedEventArgs args)
{
if (_ignoreEventsLevel != 0)
return;
OnCollectionChanged(args);
}
void _OnCurrentChanging(object sender, CurrentChangingEventArgs args)
{
if (_ignoreEventsLevel != 0)
return;
OnCurrentChanging();
}
void _OnCurrentChanged(object sender, EventArgs args)
{
if (_ignoreEventsLevel != 0)
return;
OnCurrentChanged();
}
#endregion Private Methods
#region Private Data
//-----------------------------------------------------
//
// Private Fields
//
//------------------------------------------------------
ListCollectionView _view;
ObservableCollection _snapshot;
IEnumerator _trackingEnumerator;
int _ignoreEventsLevel;
bool _pollForChanges;
bool _warningHasBeenRaised;
class IgnoreViewEventsHelper : IDisposable
{
public IgnoreViewEventsHelper(EnumerableCollectionView parent)
{
_parent = parent;
_parent.BeginIgnoreEvents();
}
public void Dispose()
{
if (_parent != null)
{
_parent.EndIgnoreEvents();
_parent = null;
}
GC.SuppressFinalize(this);
}
EnumerableCollectionView _parent;
}
#endregion Private Data
}
}
// 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: Collection view over an IEnumerable.
//
//---------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Threading;
using System.Windows;
using System.Windows.Data;
namespace MS.Internal.Data
{
///
/// Collection view over an IEnumerable.
///
internal class EnumerableCollectionView : CollectionView
{
#region Constructors
//-----------------------------------------------------
//
// Constructors
//
//-----------------------------------------------------
// Set up a ListCollectionView over the
// snapshot. We will delegate all CollectionView functionality
// to this view.
internal EnumerableCollectionView(IEnumerable source)
: base(source, -1)
{
_snapshot = new ObservableCollection();
LoadSnapshotCore(source);
if (_snapshot.Count > 0)
{
SetCurrent(_snapshot[0], 0, 1);
}
else
{
SetCurrent(null, -1, 0);
}
// if the source doesn't raise collection change events, try to
// detect changes by polling the enumerator
_pollForChanges = !(source is INotifyCollectionChanged);
_view = new ListCollectionView(_snapshot);
INotifyCollectionChanged incc = _view as INotifyCollectionChanged;
incc.CollectionChanged += new NotifyCollectionChangedEventHandler(_OnViewChanged);
INotifyPropertyChanged ipc = _view as INotifyPropertyChanged;
ipc.PropertyChanged += new PropertyChangedEventHandler(_OnPropertyChanged);
_view.CurrentChanging += new CurrentChangingEventHandler(_OnCurrentChanging);
_view.CurrentChanged += new EventHandler(_OnCurrentChanged);
}
#endregion Constructors
//------------------------------------------------------
//
// Interfaces
//
//-----------------------------------------------------
#region ICollectionView
///
/// Culture to use during sorting.
///
public override System.Globalization.CultureInfo Culture
{
get { return _view.Culture; }
set { _view.Culture = value; }
}
///
/// Return true if the item belongs to this view. No assumptions are
/// made about the item. This method will behave similarly to IList.Contains().
/// If the caller knows that the item belongs to the
/// underlying collection, it is more efficient to call PassesFilter.
///
public override bool Contains(object item)
{
EnsureSnapshot();
return _view.Contains(item);
}
///
/// Set/get a filter callback to filter out items in collection.
/// This property will always accept a filter, but the collection view for the
/// underlying InnerList or ItemsSource may not actually support filtering.
/// Please check
///
///
/// Collections assigned to ItemsSource may not support filtering and could throw a NotSupportedException.
/// Use property to test if sorting is supported before adding
/// to SortDescriptions.
///
public override Predicate Filter
{
get { return _view.Filter; }
set { _view.Filter = value; }
}
///
/// Test if this ICollectionView supports filtering before assigning
/// a filter callback to .
///
public override bool CanFilter
{
get { return _view.CanFilter; }
}
///
/// Set/get Sort criteria to sort items in collection.
///
///
///
/// Clear a sort criteria by assigning SortDescription.Empty to this property.
/// One or more sort criteria in form of
/// can be used, each specifying a property and direction to sort by.
///
///
///
/// Simpler implementations do not support sorting and will throw a NotSupportedException.
/// Use property to test if sorting is supported before adding
/// to SortDescriptions.
///
public override SortDescriptionCollection SortDescriptions
{
get { return _view.SortDescriptions; }
}
///
/// Test if this ICollectionView supports sorting before adding
/// to .
///
public override bool CanSort
{
get { return _view.CanSort; }
}
///
/// Returns true if this view really supports grouping.
/// When this returns false, the rest of the interface is ignored.
///
public override bool CanGroup
{
get { return _view.CanGroup; }
}
///
/// The description of grouping, indexed by level.
///
public override ObservableCollection GroupDescriptions
{
get { return _view.GroupDescriptions; }
}
///
/// The top-level groups, constructed according to the descriptions
/// given in GroupDescriptions.
///
public override ReadOnlyObservableCollection Groups
{
get { return _view.Groups; }
}
///
/// Enter a Defer Cycle.
/// Defer cycles are used to coalesce changes to the ICollectionView.
///
public override IDisposable DeferRefresh()
{
return _view.DeferRefresh();
}
/// Return current item.
public override object CurrentItem
{
get { return _view.CurrentItem; }
}
///
/// The ordinal position of the within the (optionally
/// sorted and filtered) view.
///
public override int CurrentPosition
{
get { return _view.CurrentPosition; }
}
/// Return true if currency is beyond the end (End-Of-File).
public override bool IsCurrentAfterLast
{
get { return _view.IsCurrentAfterLast; }
}
/// Return true if currency is before the beginning (Beginning-Of-File).
public override bool IsCurrentBeforeFirst
{
get { return _view.IsCurrentBeforeFirst; }
}
/// Move to the first item.
public override bool MoveCurrentToFirst()
{
return _view.MoveCurrentToFirst();
}
/// Move to the previous item.
public override bool MoveCurrentToPrevious()
{
return _view.MoveCurrentToPrevious();
}
/// Move to the next item.
public override bool MoveCurrentToNext()
{
return _view.MoveCurrentToNext();
}
/// Move to the last item.
public override bool MoveCurrentToLast()
{
return _view.MoveCurrentToLast();
}
/// Move to the given item.
public override bool MoveCurrentTo(object item)
{
return _view.MoveCurrentTo(item);
}
/// Move CurrentItem to this index
public override bool MoveCurrentToPosition(int position)
{
//
// If the index is out of range here, I'll let the
// _view be the one to make that determination.
//
return _view.MoveCurrentToPosition(position);
}
#endregion ICollectionView
//------------------------------------------------------
//
// Public Properties
//
//------------------------------------------------------
#region Public Properties
///
/// Return the number of records (or -1, meaning "don't know").
/// A virtualizing view should return the best estimate it can
/// without de-virtualizing all the data. A non-virtualizing view
/// should return the exact count of its (filtered) data.
///
public override int Count
{
get
{
EnsureSnapshot();
return _view.Count;
}
}
public override bool IsEmpty
{
get
{
EnsureSnapshot();
return (_view != null) ? _view.IsEmpty : true;
}
}
///
/// Returns true if this view needs to be refreshed.
///
public override bool NeedsRefresh
{
get { return _view.NeedsRefresh; }
}
#endregion Public Properties
//-----------------------------------------------------
//
// Public Methods
//
//------------------------------------------------------
#region Public Methods
/// Return the index where the given item appears, or -1 if doesn't appear.
///
/// data item
public override int IndexOf(object item)
{
EnsureSnapshot();
return _view.IndexOf(item);
}
///
/// Return true if the item belongs to this view. The item is assumed to belong to the
/// underlying DataCollection; this method merely takes filters into account.
/// It is commonly used during collection-changed notifications to determine if the added/removed
/// item requires processing.
/// Returns true if no filter is set on collection view.
///
public override bool PassesFilter(object item)
{
if (_view.CanFilter && _view.Filter != null)
return _view.Filter(item);
return true;
}
///
/// Retrieve item at the given zero-based index in this CollectionView.
///
///
/// Thrown if index is out of range
///
public override object GetItemAt(int index)
{
EnsureSnapshot();
return _view.GetItemAt(index);
}
#endregion Public Methods
//-----------------------------------------------------
//
// Protected Methods
//
//-----------------------------------------------------
#region Protected Methods
/// Implementation of IEnumerable.GetEnumerator().
/// This provides a way to enumerate the members of the collection
/// without changing the currency.
///
protected override IEnumerator GetEnumerator()
{
EnsureSnapshot();
return ((IEnumerable) _view).GetEnumerator();
}
/// Re-create the view, using any .
protected override void RefreshOverride()
{
LoadSnapshot(SourceCollection);
}
///
/// Must be implemented by the derived classes to process a single change on the
/// UI thread. The UI thread will have already been entered by now.
///
///
/// The NotifyCollectionChangedEventArgs to be processed.
///
protected override void ProcessCollectionChanged(NotifyCollectionChangedEventArgs args)
{
// ignore events received during initialization
if (_view == null)
return;
// apply the change to the snapshot
switch (args.Action)
{
case NotifyCollectionChangedAction.Add:
if (args.NewStartingIndex < 0 || _snapshot.Count <= args.NewStartingIndex)
{ // append
for (int i=0; i=0; --i)
{
_snapshot.Insert(args.NewStartingIndex, args.NewItems[i]);
}
}
break;
case NotifyCollectionChangedAction.Remove:
if (args.OldStartingIndex < 0)
throw new InvalidOperationException(SR.Get(SRID.RemovedItemNotFound));
for (int i=args.OldItems.Count-1, index=args.OldStartingIndex+i; i>=0; --i, --index)
{
if (!Object.Equals(args.OldItems[i], _snapshot[index]))
//
throw new InvalidOperationException(SR.Get(SRID.AddedItemNotAtIndex, index));
_snapshot.RemoveAt(index);
}
break;
case NotifyCollectionChangedAction.Replace:
for (int i=args.NewItems.Count-1, index=args.NewStartingIndex+i; i>=0; --i, --index)
{
if (!Object.Equals(args.OldItems[i], _snapshot[index]))
//
throw new InvalidOperationException(SR.Get(SRID.AddedItemNotAtIndex, index));
_snapshot[index] = args.NewItems[i];
}
break;
case NotifyCollectionChangedAction.Move:
if (args.NewStartingIndex < 0)
throw new InvalidOperationException(SR.Get(SRID.CannotMoveToUnknownPosition));
if (args.OldStartingIndex < args.NewStartingIndex)
{
for (int i = args.OldItems.Count - 1,
oldIndex = args.OldStartingIndex + i,
newIndex = args.NewStartingIndex + i;
i >= 0;
--i, --oldIndex, --newIndex)
{
if (!Object.Equals(args.OldItems[i], _snapshot[oldIndex]))
//
throw new InvalidOperationException(SR.Get(SRID.AddedItemNotAtIndex, oldIndex));
_snapshot.Move(oldIndex, newIndex);
}
}
else
{
for (int i = 0,
oldIndex = args.OldStartingIndex + i,
newIndex = args.NewStartingIndex + i;
i < args.OldItems.Count;
++i, ++oldIndex, ++newIndex)
{
if (!Object.Equals(args.OldItems[i], _snapshot[oldIndex]))
//
throw new InvalidOperationException(SR.Get(SRID.AddedItemNotAtIndex, oldIndex));
_snapshot.Move(oldIndex, newIndex);
}
}
break;
case NotifyCollectionChangedAction.Reset:
LoadSnapshot(SourceCollection);
break;
}
}
#endregion Protected Methods
//-----------------------------------------------------
//
// Private Methods
//
//------------------------------------------------------
#region Private Methods
// Load a snapshot of the contents of the IEnumerable into the
// ObservableCollection.
void LoadSnapshot(IEnumerable source)
{
// force currency off the collection (gives user a chance to save dirty information)
OnCurrentChanging();
// remember the values of the scalar properties, so that we can restore
// them and raise events after reloading the data
object oldCurrentItem = CurrentItem;
int oldCurrentPosition = CurrentPosition;
bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
bool oldIsCurrentAfterLast = IsCurrentAfterLast;
// reload the data
LoadSnapshotCore(source);
// tell listeners everything has changed
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
OnCurrentChanged();
if (IsCurrentAfterLast != oldIsCurrentAfterLast)
OnPropertyChanged(new PropertyChangedEventArgs(IsCurrentAfterLastPropertyName));
if (IsCurrentBeforeFirst != oldIsCurrentBeforeFirst)
OnPropertyChanged(new PropertyChangedEventArgs(IsCurrentBeforeFirstPropertyName));
if (oldCurrentPosition != CurrentPosition)
OnPropertyChanged(new PropertyChangedEventArgs(CurrentPositionPropertyName));
if (oldCurrentItem != CurrentItem)
OnPropertyChanged(new PropertyChangedEventArgs(CurrentItemPropertyName));
}
void LoadSnapshotCore(IEnumerable source)
{
_trackingEnumerator = source.GetEnumerator();
using (IgnoreViewEvents())
{
_snapshot.Clear();
while (_trackingEnumerator.MoveNext())
{
_snapshot.Add(_trackingEnumerator.Current);
}
}
}
// if the IEnumerable has changed, bring the snapshot up to date.
// (This isn't necessary if the IEnumerable is also INotifyCollectionChanged
// because we keep the snapshot in [....] incrementally.)
void EnsureSnapshot()
{
if (_pollForChanges)
{
try
{
_trackingEnumerator.MoveNext();
}
catch (InvalidOperationException)
{
//
if (TraceData.IsEnabled && !_warningHasBeenRaised)
{
_warningHasBeenRaised = true;
TraceData.Trace(TraceEventType.Warning,
TraceData.CollectionChangedWithoutNotification(SourceCollection.GetType().FullName));
}
// collection was changed - start over with a new enumerator
LoadSnapshotCore(SourceCollection);
}
}
}
IDisposable IgnoreViewEvents()
{
return new IgnoreViewEventsHelper(this);
}
void BeginIgnoreEvents()
{
++ _ignoreEventsLevel;
}
void EndIgnoreEvents()
{
-- _ignoreEventsLevel;
}
// forward events from the internal view to our own listeners
void _OnPropertyChanged(object sender, PropertyChangedEventArgs args)
{
if (_ignoreEventsLevel != 0)
return;
OnPropertyChanged(args);
}
void _OnViewChanged(object sender, NotifyCollectionChangedEventArgs args)
{
if (_ignoreEventsLevel != 0)
return;
OnCollectionChanged(args);
}
void _OnCurrentChanging(object sender, CurrentChangingEventArgs args)
{
if (_ignoreEventsLevel != 0)
return;
OnCurrentChanging();
}
void _OnCurrentChanged(object sender, EventArgs args)
{
if (_ignoreEventsLevel != 0)
return;
OnCurrentChanged();
}
#endregion Private Methods
#region Private Data
//-----------------------------------------------------
//
// Private Fields
//
//------------------------------------------------------
ListCollectionView _view;
ObservableCollection _snapshot;
IEnumerator _trackingEnumerator;
int _ignoreEventsLevel;
bool _pollForChanges;
bool _warningHasBeenRaised;
class IgnoreViewEventsHelper : IDisposable
{
public IgnoreViewEventsHelper(EnumerableCollectionView parent)
{
_parent = parent;
_parent.BeginIgnoreEvents();
}
public void Dispose()
{
if (_parent != null)
{
_parent.EndIgnoreEvents();
_parent = null;
}
GC.SuppressFinalize(this);
}
EnumerableCollectionView _parent;
}
#endregion Private Data
}
}
// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.