Code:
/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / wpf / src / Framework / MS / Internal / Controls / InnerItemCollectionView.cs / 1305600 / InnerItemCollectionView.cs
//----------------------------------------------------------------------------
//
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//
// Description: inner item collection view used by ItemsControl.
// This is a "cached" view which allows modifications while the view is
// sorted/filtered; it does not keep the view in sorted/filtered state.
//
// See specs at http://avalon/connecteddata/Specs/ItemsControl.mht
//
//---------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using MS.Internal.Data;
namespace MS.Internal.Controls
{
internal sealed class InnerItemCollectionView : CollectionView, IList
{
// InnerItemCollectionView will return itself as SourceCollection (SourceCollection property is overridden);
// shouldProcessCollectionChanged is turned off because this class will handle its own events.
public InnerItemCollectionView(int capacity, ItemCollection itemCollection)
: base(EmptyEnumerable.Instance, false)
{
// This list is cloned and diverged when Sort/Filter is applied.
_rawList = _viewList = new ArrayList(capacity);
_itemCollection = itemCollection;
}
//-----------------------------------------------------
//
// Public Interfaces
//
//-----------------------------------------------------
#region ICollectionView
///
/// Collection of Sort criteria to sort items in this view over the SourceCollection.
///
///
///
/// Simpler implementations do not support sorting and will return an empty
/// and immutable / read-only SortDescription collection.
/// Attempting to modify such a collection will cause NotSupportedException.
/// Use property on CollectionView to test if sorting is supported
/// before modifying the returned collection.
///
///
/// One or more sort criteria in form of
/// can be added, each specifying a property and direction to sort by.
///
///
public override SortDescriptionCollection SortDescriptions
{
get
{
if (_sort == null)
SetSortDescriptions(new SortDescriptionCollection());
return _sort;
}
}
///
/// Test if this ICollectionView supports sorting before adding
/// to .
///
public override bool CanSort
{
get { return true; }
}
///
/// 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 view.
/// 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)
{
return _viewList.Contains(item);
}
#endregion ICollectionView
#region IList
/// Gets or sets the element at the specified index.
/// The zero-based index of the element to get or set.
/// The element at the specified index.
public object this[int index]
{
get
{
return GetItemAt(index);
}
set
{
// will throw an exception if item already has a model parent
DependencyObject node = AssertPristineModelChild(value);
bool changingCurrentItem = (CurrentPosition == index);
// getter checks index and will throw out of range exception
object originalItem = _viewList[index];
// add new item into list for now, but might be rolled back if things go wrong
_viewList[index] = value;
int originalIndexR = -1;
if (IsCachedMode)
{
originalIndexR = _rawList.IndexOf(originalItem);
_rawList[originalIndexR] = value;
}
// try setting model parent, be prepared to rollback item from ItemCollection
bool isAddSuccessful = true;
if (node != null)
{
isAddSuccessful = false;
try
{
SetModelParent(value);
isAddSuccessful = true;
}
finally
{
if (!isAddSuccessful)
{
// failed to set new model parent, back new item out of collection
// and keep old item in collection (note: its parent hasn't been cleared yet!)
_viewList[index] = originalItem;
if (originalIndexR > 0)
{
_rawList[originalIndexR] = originalItem;
}
}
else
{
// was able to parent new item, now cleanup old item
ClearModelParent(originalItem);
}
}
}
if (!isAddSuccessful)
return;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, originalItem, index));
SetIsModified();
}
}
public bool IsReadOnly
{
get { return false; }
}
public bool IsFixedSize
{
get { return false; }
}
// always adds at the end of list and view
public int Add(object item)
{
// will throw an exception if item already has a model parent
DependencyObject node = AssertPristineModelChild(item);
// add to collection before attempting to set model parent
int indexV = _viewList.Add(item);
int indexR = -1;
if (IsCachedMode)
{
indexR = _rawList.Add(item);
}
// try setting model parent, be prepared to rollback item from ItemCollection
bool isAddSuccessful = true;
if (node != null)
{
isAddSuccessful = false;
try
{
SetModelParent(item);
isAddSuccessful = true;
}
finally
{
if (!isAddSuccessful)
{
// failed to set new model parent, back item out of collection
_viewList.RemoveAt(indexV);
if (indexR >= 0)
{
_rawList.RemoveAt(indexR);
}
// also roll back the parent set
ClearModelParent(item);
indexV = -1;
}
}
}
if (!isAddSuccessful)
return -1;
AdjustCurrencyForAdd(indexV);
SetIsModified();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, indexV));
return indexV;
}
///
/// Clears the collection. Releases the references on all items
/// currently in the collection.
///
///
/// the ItemCollection is read-only because it is in ItemsSource mode
///
public void Clear()
{
try
{
for (int i = _rawList.Count - 1; i >=0; --i)
{
ClearModelParent(_rawList[i]);
}
}
finally
{
_rawList.Clear();
// Refresh will [....] the _viewList to the cleared _rawList
RefreshOrDefer();
}
}
public void Insert(int index, object item)
{
// will throw an exception if item already has a model parent
DependencyObject node = AssertPristineModelChild(item);
// add to collection before attempting to set model parent
_viewList.Insert(index, item);
int indexR = -1;
if (IsCachedMode)
{
indexR = _rawList.Add(item);
}
// try setting model parent, be prepared to rollback item from ItemCollection
bool isAddSuccessful = true;
if (node != null)
{
isAddSuccessful = false;
try
{
SetModelParent(item);
isAddSuccessful = true;
}
finally
{
if (!isAddSuccessful)
{
// failed to set new model parent, back item out of collection
_viewList.RemoveAt(index);
if (indexR >= 0)
{
_rawList.RemoveAt(indexR);
}
// also roll back the parent set
ClearModelParent(item);
}
}
}
if (!isAddSuccessful)
return;
AdjustCurrencyForAdd(index);
SetIsModified();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
}
public void Remove(object item)
{
int indexV = _viewList.IndexOf(item);
int indexR = -1;
if (IsCachedMode)
{
indexR = _rawList.IndexOf(item);
}
_RemoveAt(indexV, indexR, item);
}
public void RemoveAt(int index)
{
if ((0 <= index) && (index < ViewCount))
{
object item = this[index];
int indexR = -1;
if (IsCachedMode)
{
indexR = _rawList.IndexOf(item);
}
_RemoveAt(index, indexR, item);
}
else
{
throw new ArgumentOutOfRangeException("index",
SR.Get(SRID.ItemCollectionRemoveArgumentOutOfRange));
}
}
#endregion IList
#region ICollection
///
/// Gets a value indicating whether access to the collection is synchronized (thread-safe).
///
/// true if access to the collection is synchronized (thread-safe); otherwise, false.
bool ICollection.IsSynchronized
{
get { return false; }
}
///
/// Gets an object that can be used to synchronize access to the view
///
/// an object that can be used to synchronize access to the view
object ICollection.SyncRoot
{
get { return _rawList.SyncRoot; }
}
///
/// Copies all the elements of the current collection (view) to the specified one-dimensional Array.
///
void ICollection.CopyTo(Array array, int index)
{
_viewList.CopyTo(array, index);
}
#endregion ICollection
//------------------------------------------------------
//
// Public Properties
//
//-----------------------------------------------------
#region Public Properties
public override IEnumerable SourceCollection
{
get { return this; }
}
///
/// Return the number of records in (filtered) view
///
public override int Count
{
get
{
return ViewCount;
}
}
///
/// Returns true if the resulting (filtered) view is emtpy.
///
public override bool IsEmpty
{
get
{
return ViewCount == 0;
}
}
public override bool NeedsRefresh
{
get
{
return base.NeedsRefresh || _isModified;
}
}
#endregion Public Properties
//------------------------------------------------------
//
// Public Methods
//
//------------------------------------------------------
#region Public Methods
/// Return the index where the given item belongs, or -1 if this index is unknown.
///
///
/// If this method returns an index other than -1, it must always be true that
/// view[index-1] < item <= view[index], where the comparisons are done via
/// the view's IComparer.Compare method (if any).
/// (This method is used by a listener's (e.g. System.Windows.Controls.ItemsControl)
/// CollectionChanged event handler to speed up its reaction to insertion and deletion of items.
/// If IndexOf is not implemented, a listener does a binary search using IComparer.Compare.)
///
/// data item
public override int IndexOf(object item)
{
return _viewList.IndexOf(item);
}
///
/// 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)
{
return _viewList[index];
}
///
/// Move to the given item.
/// If the item is not found, move to BeforeFirst.
///
/// Move CurrentItem to this item.
/// true if points to an item within the view.
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;
}
return MoveCurrentToPosition(IndexOf(item));
}
///
/// Move to the item at the given index.
///
/// Move CurrentItem to this index
/// true if points to an item within the view.
public override bool MoveCurrentToPosition(int position)
{
if (position < -1 || position > ViewCount)
throw new ArgumentOutOfRangeException("position");
if (position != CurrentPosition && OKToChangeCurrent())
{
bool oldIsCurrentAfterLast = IsCurrentAfterLast;
bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
_MoveCurrentToPosition(position);
OnCurrentChanged();
if (IsCurrentAfterLast != oldIsCurrentAfterLast)
OnPropertyChanged(IsCurrentAfterLastPropertyName);
if (IsCurrentBeforeFirst != oldIsCurrentBeforeFirst)
OnPropertyChanged(IsCurrentBeforeFirstPropertyName);
OnPropertyChanged(CurrentPositionPropertyName);
OnPropertyChanged(CurrentItemPropertyName);
}
return IsCurrentInView;
}
#endregion Public Methods
//-----------------------------------------------------
//
// Protected Methods
//
//------------------------------------------------------
#region Protected Methods
///
/// Re-create the view, using any and/or .
///
protected override void RefreshOverride()
{
bool wasEmpty = IsEmpty;
object oldCurrentItem = CurrentItem;
bool oldIsCurrentAfterLast = IsCurrentAfterLast;
bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
int oldCurrentPosition = CurrentPosition;
// force currency off the collection (gives user a chance to save dirty information)
OnCurrentChanging();
if (SortDescriptions.Count > 0 || Filter != null)
{
// filter the view list
if (Filter == null)
{
_viewList = new ArrayList(_rawList);
}
else
{
// optimized for footprint: initialize to size 0 and let AL amortize cost of growth
_viewList = new ArrayList();
for (int k = 0; k < _rawList.Count; ++k)
{
if (Filter(_rawList[k]))
_viewList.Add(_rawList[k]);
}
}
// sort the view list
if (_sort != null && _sort.Count > 0 && ViewCount > 0)
{
SortFieldComparer.SortHelper(_viewList, new SortFieldComparer(_sort, Culture));
}
}
else // no sort or filter
{
_viewList = _rawList;
}
if (IsEmpty || oldIsCurrentBeforeFirst)
{
_MoveCurrentToPosition(-1);
}
else if (oldIsCurrentAfterLast)
{
_MoveCurrentToPosition(ViewCount);
}
else if (oldCurrentItem != null) // set currency back to old current item, or first if not found
{
int index = _viewList.IndexOf(oldCurrentItem);
if (index < 0)
{
index = 0;
}
_MoveCurrentToPosition(index);
}
ClearIsModified();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
OnCurrentChanged();
if (IsCurrentAfterLast != oldIsCurrentAfterLast)
OnPropertyChanged(IsCurrentAfterLastPropertyName);
if (IsCurrentBeforeFirst != oldIsCurrentBeforeFirst)
OnPropertyChanged(IsCurrentBeforeFirstPropertyName);
if (oldCurrentPosition != CurrentPosition)
OnPropertyChanged(CurrentPositionPropertyName);
if (oldCurrentItem != CurrentItem)
OnPropertyChanged(CurrentItemPropertyName);
}
///
/// Returns an object that enumerates the items in this view.
///
protected override IEnumerator GetEnumerator()
{
return _viewList.GetEnumerator();
}
#endregion Protected Methods
//-----------------------------------------------------
//
// Internal Properties
//
//-----------------------------------------------------
#region Internal Properties
internal ItemCollection ItemCollection
{
get { return _itemCollection; }
}
internal IEnumerator LogicalChildren
{
get
{
return _rawList.GetEnumerator();
}
}
#endregion Internal Properties
//-----------------------------------------------------
//
// Private Properties
//
//------------------------------------------------------
#region Private Properties
internal int RawCount
{
get { return _rawList.Count; }
}
private int ViewCount
{
get { return _viewList.Count; }
}
// Cached Mode is when two lists are maintained.
private bool IsCachedMode
{
get { return _viewList != _rawList; }
}
private FrameworkElement ModelParentFE
{
get { return ItemCollection.ModelParentFE; }
}
private bool IsCurrentInView
{
get
{
return (0 <= CurrentPosition && CurrentPosition < ViewCount);
}
}
#endregion Private Properties
//-----------------------------------------------------
//
// Private Methods
//
//------------------------------------------------------
#region Private Methods
// called when making any modifying action on the collection that could cause a refresh to be needed.
private void SetIsModified()
{
if (IsCachedMode)
_isModified = true;
}
private void ClearIsModified()
{
_isModified = false;
}
private void _RemoveAt(int index, int indexR, object item)
{
if (index >=0)
_viewList.RemoveAt(index);
if (indexR >= 0)
_rawList.RemoveAt(indexR);
try
{
// removing the model parent could throw, but we'd be left in a consistent state:
// item is already removed from this collection when unparenting throws
ClearModelParent(item);
}
finally
{
if (index >= 0)
{
AdjustCurrencyForRemove(index);
//SetIsModified(); // A Remove is not affected by the view's sort and filter
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
// currency has to change after firing the deletion event,
// so event handlers have the right picture
if (_currentElementWasRemoved)
{
MoveCurrencyOffDeletedElement();
}
}
}
}
// check that item is not already parented
// throws an exception if already parented
DependencyObject AssertPristineModelChild(object item)
{
DependencyObject node = item as DependencyObject;
if (node == null)
{
return null;
}
// refuse a child which already has a different model parent!
// NOTE: model tree spec would allow reparenting if the parent does not change
// but this code will throw: this is a efficient way to catch
// an attempt to add the same element twice to the collection
if (LogicalTreeHelper.GetParent(node) != null)
{
throw new InvalidOperationException(SR.Get(SRID.ReparentModelChildIllegal));
}
return node;
}
// NOTE: Only change the item's logical links if the host is a Visual (bug 986386)
void SetModelParent(object item)
{
// to avoid the unnecessary, expensive code in AddLogicalChild, check for DO first
if ((ModelParentFE != null) && (item is DependencyObject))
LogicalTreeHelper.AddLogicalChild(ModelParentFE, null, item);
}
// if item implements IModelTree, clear model parent
void ClearModelParent(object item)
{
// ClearModelParent is also called for items that are not a DependencyObject;
// to avoid the unnecessary, expensive code in RemoveLogicalChild, check for DO first
if ((ModelParentFE != null) && (item is DependencyObject))
LogicalTreeHelper.RemoveLogicalChild(ModelParentFE, null, item);
}
// set new SortDescription collection; rehook collection change notification handler
private void SetSortDescriptions(SortDescriptionCollection descriptions)
{
if (_sort != null)
{
((INotifyCollectionChanged)_sort).CollectionChanged -= new NotifyCollectionChangedEventHandler(SortDescriptionsChanged);
}
_sort = descriptions;
if (_sort != null)
{
Invariant.Assert(_sort.Count == 0, "must be empty SortDescription collection");
((INotifyCollectionChanged)_sort).CollectionChanged += new NotifyCollectionChangedEventHandler(SortDescriptionsChanged);
}
}
// SortDescription was added/removed, refresh CollectionView
private void SortDescriptionsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
RefreshOrDefer();
}
// Just move it. No argument check, no events, just move current to position.
private void _MoveCurrentToPosition(int position)
{
if (position < 0)
{
SetCurrent(null, -1);
}
else if (position >= ViewCount)
{
SetCurrent(null, ViewCount);
}
else
{
SetCurrent(_viewList[position], position);
}
}
// fix up CurrentPosition and CurrentItem after a collection change
private void AdjustCurrencyForAdd(int index)
{
if (index < 0)
return;
if (ViewCount == 1)
{
// added first item; set current at BeforeFirst
SetCurrent(null, -1);
}
else if (index <= CurrentPosition) // adjust current index if insertion is earlier
{
int newCurrentPosition = CurrentPosition + 1;
if (newCurrentPosition < ViewCount)
{
// CurrentItem might be out of [....] if underlying list is not INCC
// or if this Add is the result of a Replace (Rem + Add)
SetCurrent(_viewList[newCurrentPosition], newCurrentPosition);
}
else
{
SetCurrent(null, ViewCount);
}
}
}
// fix up CurrentPosition and CurrentItem after a collection change
private void AdjustCurrencyForRemove(int index)
{
if (index < 0)
return;
// adjust current index if deletion is earlier
if (index < CurrentPosition)
{
int newCurrentPosition = CurrentPosition - 1;
SetCurrent(_viewList[newCurrentPosition], newCurrentPosition);
}
// move currency off the deleted element
else if (index == CurrentPosition)
{
_currentElementWasRemoved = true;
}
}
// set CurrentItem to the item at CurrentPosition
private void MoveCurrencyOffDeletedElement()
{
int lastPosition = ViewCount - 1; // OK if last is -1
// if position falls beyond last position, move back to last position
int newPosition = (CurrentPosition < lastPosition) ? CurrentPosition : lastPosition;
// reset this before raising events to avoid problems in re-entrancy
_currentElementWasRemoved = false;
// ignore cancel, there's no choice in this currency change
OnCurrentChanging();
// update CurrentItem to match new position
_MoveCurrentToPosition(newPosition);
OnCurrentChanged();
}
///
/// Helper to raise a PropertyChanged event />).
///
private void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
#endregion Private Methods
//------------------------------------------------------
//
// Private Fields
//
//-----------------------------------------------------
SortDescriptionCollection _sort;
ArrayList _viewList, _rawList;
ItemCollection _itemCollection;
bool _isModified;
bool _currentElementWasRemoved = false; // true if we need to MoveCurrencyOffDeletedElement
}
}
// 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: inner item collection view used by ItemsControl.
// This is a "cached" view which allows modifications while the view is
// sorted/filtered; it does not keep the view in sorted/filtered state.
//
// See specs at http://avalon/connecteddata/Specs/ItemsControl.mht
//
//---------------------------------------------------------------------------
using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using MS.Internal.Data;
namespace MS.Internal.Controls
{
internal sealed class InnerItemCollectionView : CollectionView, IList
{
// InnerItemCollectionView will return itself as SourceCollection (SourceCollection property is overridden);
// shouldProcessCollectionChanged is turned off because this class will handle its own events.
public InnerItemCollectionView(int capacity, ItemCollection itemCollection)
: base(EmptyEnumerable.Instance, false)
{
// This list is cloned and diverged when Sort/Filter is applied.
_rawList = _viewList = new ArrayList(capacity);
_itemCollection = itemCollection;
}
//-----------------------------------------------------
//
// Public Interfaces
//
//-----------------------------------------------------
#region ICollectionView
///
/// Collection of Sort criteria to sort items in this view over the SourceCollection.
///
///
///
/// Simpler implementations do not support sorting and will return an empty
/// and immutable / read-only SortDescription collection.
/// Attempting to modify such a collection will cause NotSupportedException.
/// Use property on CollectionView to test if sorting is supported
/// before modifying the returned collection.
///
///
/// One or more sort criteria in form of
/// can be added, each specifying a property and direction to sort by.
///
///
public override SortDescriptionCollection SortDescriptions
{
get
{
if (_sort == null)
SetSortDescriptions(new SortDescriptionCollection());
return _sort;
}
}
///
/// Test if this ICollectionView supports sorting before adding
/// to .
///
public override bool CanSort
{
get { return true; }
}
///
/// 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 view.
/// 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)
{
return _viewList.Contains(item);
}
#endregion ICollectionView
#region IList
/// Gets or sets the element at the specified index.
/// The zero-based index of the element to get or set.
/// The element at the specified index.
public object this[int index]
{
get
{
return GetItemAt(index);
}
set
{
// will throw an exception if item already has a model parent
DependencyObject node = AssertPristineModelChild(value);
bool changingCurrentItem = (CurrentPosition == index);
// getter checks index and will throw out of range exception
object originalItem = _viewList[index];
// add new item into list for now, but might be rolled back if things go wrong
_viewList[index] = value;
int originalIndexR = -1;
if (IsCachedMode)
{
originalIndexR = _rawList.IndexOf(originalItem);
_rawList[originalIndexR] = value;
}
// try setting model parent, be prepared to rollback item from ItemCollection
bool isAddSuccessful = true;
if (node != null)
{
isAddSuccessful = false;
try
{
SetModelParent(value);
isAddSuccessful = true;
}
finally
{
if (!isAddSuccessful)
{
// failed to set new model parent, back new item out of collection
// and keep old item in collection (note: its parent hasn't been cleared yet!)
_viewList[index] = originalItem;
if (originalIndexR > 0)
{
_rawList[originalIndexR] = originalItem;
}
}
else
{
// was able to parent new item, now cleanup old item
ClearModelParent(originalItem);
}
}
}
if (!isAddSuccessful)
return;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, value, originalItem, index));
SetIsModified();
}
}
public bool IsReadOnly
{
get { return false; }
}
public bool IsFixedSize
{
get { return false; }
}
// always adds at the end of list and view
public int Add(object item)
{
// will throw an exception if item already has a model parent
DependencyObject node = AssertPristineModelChild(item);
// add to collection before attempting to set model parent
int indexV = _viewList.Add(item);
int indexR = -1;
if (IsCachedMode)
{
indexR = _rawList.Add(item);
}
// try setting model parent, be prepared to rollback item from ItemCollection
bool isAddSuccessful = true;
if (node != null)
{
isAddSuccessful = false;
try
{
SetModelParent(item);
isAddSuccessful = true;
}
finally
{
if (!isAddSuccessful)
{
// failed to set new model parent, back item out of collection
_viewList.RemoveAt(indexV);
if (indexR >= 0)
{
_rawList.RemoveAt(indexR);
}
// also roll back the parent set
ClearModelParent(item);
indexV = -1;
}
}
}
if (!isAddSuccessful)
return -1;
AdjustCurrencyForAdd(indexV);
SetIsModified();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, indexV));
return indexV;
}
///
/// Clears the collection. Releases the references on all items
/// currently in the collection.
///
///
/// the ItemCollection is read-only because it is in ItemsSource mode
///
public void Clear()
{
try
{
for (int i = _rawList.Count - 1; i >=0; --i)
{
ClearModelParent(_rawList[i]);
}
}
finally
{
_rawList.Clear();
// Refresh will [....] the _viewList to the cleared _rawList
RefreshOrDefer();
}
}
public void Insert(int index, object item)
{
// will throw an exception if item already has a model parent
DependencyObject node = AssertPristineModelChild(item);
// add to collection before attempting to set model parent
_viewList.Insert(index, item);
int indexR = -1;
if (IsCachedMode)
{
indexR = _rawList.Add(item);
}
// try setting model parent, be prepared to rollback item from ItemCollection
bool isAddSuccessful = true;
if (node != null)
{
isAddSuccessful = false;
try
{
SetModelParent(item);
isAddSuccessful = true;
}
finally
{
if (!isAddSuccessful)
{
// failed to set new model parent, back item out of collection
_viewList.RemoveAt(index);
if (indexR >= 0)
{
_rawList.RemoveAt(indexR);
}
// also roll back the parent set
ClearModelParent(item);
}
}
}
if (!isAddSuccessful)
return;
AdjustCurrencyForAdd(index);
SetIsModified();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
}
public void Remove(object item)
{
int indexV = _viewList.IndexOf(item);
int indexR = -1;
if (IsCachedMode)
{
indexR = _rawList.IndexOf(item);
}
_RemoveAt(indexV, indexR, item);
}
public void RemoveAt(int index)
{
if ((0 <= index) && (index < ViewCount))
{
object item = this[index];
int indexR = -1;
if (IsCachedMode)
{
indexR = _rawList.IndexOf(item);
}
_RemoveAt(index, indexR, item);
}
else
{
throw new ArgumentOutOfRangeException("index",
SR.Get(SRID.ItemCollectionRemoveArgumentOutOfRange));
}
}
#endregion IList
#region ICollection
///
/// Gets a value indicating whether access to the collection is synchronized (thread-safe).
///
/// true if access to the collection is synchronized (thread-safe); otherwise, false.
bool ICollection.IsSynchronized
{
get { return false; }
}
///
/// Gets an object that can be used to synchronize access to the view
///
/// an object that can be used to synchronize access to the view
object ICollection.SyncRoot
{
get { return _rawList.SyncRoot; }
}
///
/// Copies all the elements of the current collection (view) to the specified one-dimensional Array.
///
void ICollection.CopyTo(Array array, int index)
{
_viewList.CopyTo(array, index);
}
#endregion ICollection
//------------------------------------------------------
//
// Public Properties
//
//-----------------------------------------------------
#region Public Properties
public override IEnumerable SourceCollection
{
get { return this; }
}
///
/// Return the number of records in (filtered) view
///
public override int Count
{
get
{
return ViewCount;
}
}
///
/// Returns true if the resulting (filtered) view is emtpy.
///
public override bool IsEmpty
{
get
{
return ViewCount == 0;
}
}
public override bool NeedsRefresh
{
get
{
return base.NeedsRefresh || _isModified;
}
}
#endregion Public Properties
//------------------------------------------------------
//
// Public Methods
//
//------------------------------------------------------
#region Public Methods
/// Return the index where the given item belongs, or -1 if this index is unknown.
///
///
/// If this method returns an index other than -1, it must always be true that
/// view[index-1] < item <= view[index], where the comparisons are done via
/// the view's IComparer.Compare method (if any).
/// (This method is used by a listener's (e.g. System.Windows.Controls.ItemsControl)
/// CollectionChanged event handler to speed up its reaction to insertion and deletion of items.
/// If IndexOf is not implemented, a listener does a binary search using IComparer.Compare.)
///
/// data item
public override int IndexOf(object item)
{
return _viewList.IndexOf(item);
}
///
/// 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)
{
return _viewList[index];
}
///
/// Move to the given item.
/// If the item is not found, move to BeforeFirst.
///
/// Move CurrentItem to this item.
/// true if points to an item within the view.
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;
}
return MoveCurrentToPosition(IndexOf(item));
}
///
/// Move to the item at the given index.
///
/// Move CurrentItem to this index
/// true if points to an item within the view.
public override bool MoveCurrentToPosition(int position)
{
if (position < -1 || position > ViewCount)
throw new ArgumentOutOfRangeException("position");
if (position != CurrentPosition && OKToChangeCurrent())
{
bool oldIsCurrentAfterLast = IsCurrentAfterLast;
bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
_MoveCurrentToPosition(position);
OnCurrentChanged();
if (IsCurrentAfterLast != oldIsCurrentAfterLast)
OnPropertyChanged(IsCurrentAfterLastPropertyName);
if (IsCurrentBeforeFirst != oldIsCurrentBeforeFirst)
OnPropertyChanged(IsCurrentBeforeFirstPropertyName);
OnPropertyChanged(CurrentPositionPropertyName);
OnPropertyChanged(CurrentItemPropertyName);
}
return IsCurrentInView;
}
#endregion Public Methods
//-----------------------------------------------------
//
// Protected Methods
//
//------------------------------------------------------
#region Protected Methods
///
/// Re-create the view, using any and/or .
///
protected override void RefreshOverride()
{
bool wasEmpty = IsEmpty;
object oldCurrentItem = CurrentItem;
bool oldIsCurrentAfterLast = IsCurrentAfterLast;
bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
int oldCurrentPosition = CurrentPosition;
// force currency off the collection (gives user a chance to save dirty information)
OnCurrentChanging();
if (SortDescriptions.Count > 0 || Filter != null)
{
// filter the view list
if (Filter == null)
{
_viewList = new ArrayList(_rawList);
}
else
{
// optimized for footprint: initialize to size 0 and let AL amortize cost of growth
_viewList = new ArrayList();
for (int k = 0; k < _rawList.Count; ++k)
{
if (Filter(_rawList[k]))
_viewList.Add(_rawList[k]);
}
}
// sort the view list
if (_sort != null && _sort.Count > 0 && ViewCount > 0)
{
SortFieldComparer.SortHelper(_viewList, new SortFieldComparer(_sort, Culture));
}
}
else // no sort or filter
{
_viewList = _rawList;
}
if (IsEmpty || oldIsCurrentBeforeFirst)
{
_MoveCurrentToPosition(-1);
}
else if (oldIsCurrentAfterLast)
{
_MoveCurrentToPosition(ViewCount);
}
else if (oldCurrentItem != null) // set currency back to old current item, or first if not found
{
int index = _viewList.IndexOf(oldCurrentItem);
if (index < 0)
{
index = 0;
}
_MoveCurrentToPosition(index);
}
ClearIsModified();
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
OnCurrentChanged();
if (IsCurrentAfterLast != oldIsCurrentAfterLast)
OnPropertyChanged(IsCurrentAfterLastPropertyName);
if (IsCurrentBeforeFirst != oldIsCurrentBeforeFirst)
OnPropertyChanged(IsCurrentBeforeFirstPropertyName);
if (oldCurrentPosition != CurrentPosition)
OnPropertyChanged(CurrentPositionPropertyName);
if (oldCurrentItem != CurrentItem)
OnPropertyChanged(CurrentItemPropertyName);
}
///
/// Returns an object that enumerates the items in this view.
///
protected override IEnumerator GetEnumerator()
{
return _viewList.GetEnumerator();
}
#endregion Protected Methods
//-----------------------------------------------------
//
// Internal Properties
//
//-----------------------------------------------------
#region Internal Properties
internal ItemCollection ItemCollection
{
get { return _itemCollection; }
}
internal IEnumerator LogicalChildren
{
get
{
return _rawList.GetEnumerator();
}
}
#endregion Internal Properties
//-----------------------------------------------------
//
// Private Properties
//
//------------------------------------------------------
#region Private Properties
internal int RawCount
{
get { return _rawList.Count; }
}
private int ViewCount
{
get { return _viewList.Count; }
}
// Cached Mode is when two lists are maintained.
private bool IsCachedMode
{
get { return _viewList != _rawList; }
}
private FrameworkElement ModelParentFE
{
get { return ItemCollection.ModelParentFE; }
}
private bool IsCurrentInView
{
get
{
return (0 <= CurrentPosition && CurrentPosition < ViewCount);
}
}
#endregion Private Properties
//-----------------------------------------------------
//
// Private Methods
//
//------------------------------------------------------
#region Private Methods
// called when making any modifying action on the collection that could cause a refresh to be needed.
private void SetIsModified()
{
if (IsCachedMode)
_isModified = true;
}
private void ClearIsModified()
{
_isModified = false;
}
private void _RemoveAt(int index, int indexR, object item)
{
if (index >=0)
_viewList.RemoveAt(index);
if (indexR >= 0)
_rawList.RemoveAt(indexR);
try
{
// removing the model parent could throw, but we'd be left in a consistent state:
// item is already removed from this collection when unparenting throws
ClearModelParent(item);
}
finally
{
if (index >= 0)
{
AdjustCurrencyForRemove(index);
//SetIsModified(); // A Remove is not affected by the view's sort and filter
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, index));
// currency has to change after firing the deletion event,
// so event handlers have the right picture
if (_currentElementWasRemoved)
{
MoveCurrencyOffDeletedElement();
}
}
}
}
// check that item is not already parented
// throws an exception if already parented
DependencyObject AssertPristineModelChild(object item)
{
DependencyObject node = item as DependencyObject;
if (node == null)
{
return null;
}
// refuse a child which already has a different model parent!
// NOTE: model tree spec would allow reparenting if the parent does not change
// but this code will throw: this is a efficient way to catch
// an attempt to add the same element twice to the collection
if (LogicalTreeHelper.GetParent(node) != null)
{
throw new InvalidOperationException(SR.Get(SRID.ReparentModelChildIllegal));
}
return node;
}
// NOTE: Only change the item's logical links if the host is a Visual (bug 986386)
void SetModelParent(object item)
{
// to avoid the unnecessary, expensive code in AddLogicalChild, check for DO first
if ((ModelParentFE != null) && (item is DependencyObject))
LogicalTreeHelper.AddLogicalChild(ModelParentFE, null, item);
}
// if item implements IModelTree, clear model parent
void ClearModelParent(object item)
{
// ClearModelParent is also called for items that are not a DependencyObject;
// to avoid the unnecessary, expensive code in RemoveLogicalChild, check for DO first
if ((ModelParentFE != null) && (item is DependencyObject))
LogicalTreeHelper.RemoveLogicalChild(ModelParentFE, null, item);
}
// set new SortDescription collection; rehook collection change notification handler
private void SetSortDescriptions(SortDescriptionCollection descriptions)
{
if (_sort != null)
{
((INotifyCollectionChanged)_sort).CollectionChanged -= new NotifyCollectionChangedEventHandler(SortDescriptionsChanged);
}
_sort = descriptions;
if (_sort != null)
{
Invariant.Assert(_sort.Count == 0, "must be empty SortDescription collection");
((INotifyCollectionChanged)_sort).CollectionChanged += new NotifyCollectionChangedEventHandler(SortDescriptionsChanged);
}
}
// SortDescription was added/removed, refresh CollectionView
private void SortDescriptionsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
RefreshOrDefer();
}
// Just move it. No argument check, no events, just move current to position.
private void _MoveCurrentToPosition(int position)
{
if (position < 0)
{
SetCurrent(null, -1);
}
else if (position >= ViewCount)
{
SetCurrent(null, ViewCount);
}
else
{
SetCurrent(_viewList[position], position);
}
}
// fix up CurrentPosition and CurrentItem after a collection change
private void AdjustCurrencyForAdd(int index)
{
if (index < 0)
return;
if (ViewCount == 1)
{
// added first item; set current at BeforeFirst
SetCurrent(null, -1);
}
else if (index <= CurrentPosition) // adjust current index if insertion is earlier
{
int newCurrentPosition = CurrentPosition + 1;
if (newCurrentPosition < ViewCount)
{
// CurrentItem might be out of [....] if underlying list is not INCC
// or if this Add is the result of a Replace (Rem + Add)
SetCurrent(_viewList[newCurrentPosition], newCurrentPosition);
}
else
{
SetCurrent(null, ViewCount);
}
}
}
// fix up CurrentPosition and CurrentItem after a collection change
private void AdjustCurrencyForRemove(int index)
{
if (index < 0)
return;
// adjust current index if deletion is earlier
if (index < CurrentPosition)
{
int newCurrentPosition = CurrentPosition - 1;
SetCurrent(_viewList[newCurrentPosition], newCurrentPosition);
}
// move currency off the deleted element
else if (index == CurrentPosition)
{
_currentElementWasRemoved = true;
}
}
// set CurrentItem to the item at CurrentPosition
private void MoveCurrencyOffDeletedElement()
{
int lastPosition = ViewCount - 1; // OK if last is -1
// if position falls beyond last position, move back to last position
int newPosition = (CurrentPosition < lastPosition) ? CurrentPosition : lastPosition;
// reset this before raising events to avoid problems in re-entrancy
_currentElementWasRemoved = false;
// ignore cancel, there's no choice in this currency change
OnCurrentChanging();
// update CurrentItem to match new position
_MoveCurrentToPosition(newPosition);
OnCurrentChanged();
}
///
/// Helper to raise a PropertyChanged event />).
///
private void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
#endregion Private Methods
//------------------------------------------------------
//
// Private Fields
//
//-----------------------------------------------------
SortDescriptionCollection _sort;
ArrayList _viewList, _rawList;
ItemCollection _itemCollection;
bool _isModified;
bool _currentElementWasRemoved = false; // true if we need to MoveCurrencyOffDeletedElement
}
}
// 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
- RepeatInfo.cs
- WebPartConnectionsEventArgs.cs
- StrokeNodeEnumerator.cs
- RelatedCurrencyManager.cs
- TextHintingModeValidation.cs
- MarkerProperties.cs
- NotifyParentPropertyAttribute.cs
- Maps.cs
- DllHostInitializer.cs
- Properties.cs
- DictionarySurrogate.cs
- XmlQueryCardinality.cs
- DefaultAssemblyResolver.cs
- MediaSystem.cs
- _NtlmClient.cs
- Profiler.cs
- HideDisabledControlAdapter.cs
- CookieProtection.cs
- NativeBuffer.cs
- TriggerAction.cs
- EmptyEnumerator.cs
- ToolStripDropDownItem.cs
- DataGridItemAttachedStorage.cs
- UInt32Converter.cs
- DebugView.cs
- AutoGeneratedField.cs
- CommentEmitter.cs
- OracleInternalConnection.cs
- NativeMethods.cs
- Activity.cs
- WeakRefEnumerator.cs
- CodeTypeParameterCollection.cs
- NumericUpDown.cs
- ConnectionStringSettingsCollection.cs
- Avt.cs
- FormClosedEvent.cs
- TdsParser.cs
- DataMemberAttribute.cs
- TileModeValidation.cs
- TargetInvocationException.cs
- InstanceDataCollection.cs
- BamlCollectionHolder.cs
- BlurBitmapEffect.cs
- DataQuery.cs
- HistoryEventArgs.cs
- Font.cs
- ItemList.cs
- ControlParameter.cs
- HebrewNumber.cs
- XmlSchemaAnnotation.cs
- RemoteWebConfigurationHostStream.cs
- RelationshipSet.cs
- AnnouncementService.cs
- SubMenuStyle.cs
- InternalCache.cs
- XmlSchemaAttribute.cs
- DataGridHeaderBorder.cs
- DataSourceUtil.cs
- DataGridViewRowsRemovedEventArgs.cs
- CommandLineParser.cs
- objectresult_tresulttype.cs
- TypeBuilderInstantiation.cs
- ConvertEvent.cs
- BorderSidesEditor.cs
- InputLanguageEventArgs.cs
- SizeKeyFrameCollection.cs
- StoryFragments.cs
- TextPattern.cs
- StyleXamlParser.cs
- InheritanceRules.cs
- XmlSchemaType.cs
- ConnectionPoolManager.cs
- OdbcConnectionHandle.cs
- HTMLTagNameToTypeMapper.cs
- SchemaNotation.cs
- BitmapEncoder.cs
- DataControlFieldCollection.cs
- SQLStringStorage.cs
- SafeArrayTypeMismatchException.cs
- StylusEditingBehavior.cs
- UIElement3DAutomationPeer.cs
- RightsManagementEncryptedStream.cs
- FontUnit.cs
- VarInfo.cs
- ManifestSignatureInformation.cs
- BlurBitmapEffect.cs
- LayoutInformation.cs
- WindowsListViewScroll.cs
- Helper.cs
- AnnotationComponentChooser.cs
- ActivityExecutorSurrogate.cs
- CheckoutException.cs
- ComplexBindingPropertiesAttribute.cs
- ColumnMapCopier.cs
- ReflectionPermission.cs
- PreservationFileWriter.cs
- OdbcDataReader.cs
- Empty.cs
- SchemaElement.cs
- MembershipSection.cs