/ DotNET / DotNET / 8.0 / untmp / WIN_WINDOWS / lh_tools_devdiv_wpf / Windows / wcp / Framework / System / Windows / Controls / ItemCollection.cs / 1 / ItemCollection.cs
//----------------------------------------------------------------------------
//
//
// Copyright (C) 2003 by Microsoft Corporation. All rights reserved.
//
//
//
// Description: ItemCollection holds the list of items that constitute the content of a ItemsControl.
//
// See specs at [....]/connecteddata/Specs/ItemsControl.mht
// [....]/coreUI/Specs/model%20tree%20--%20design.doc
//
// History:
// 05/22/2003 : [....] - Created
// 09/22/2004 : [....] - moved aggregating functionality to CompositeCollection
//
//---------------------------------------------------------------------------
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.Windows;
using System.Windows.Data; // for CollectionContainer
using System.Windows.Markup;
using MS.Utility;
using MS.Internal;
using MS.Internal.Controls;
using MS.Internal.Data; // for IndexedEnumerable
using MS.Internal.KnownBoxes; // for BooleanBoxes
using MS.Internal.Utility;
namespace System.Windows.Controls
{
///
/// ItemCollection will contain items shaped as strings, objects, xml nodes,
/// elements, as well as other collections. (It will not promote elements from
/// contained collections; to "flatten" contained collections, assign a
/// to
/// the ItemsSource property on the ItemsControl.)
/// A uses the data
/// in the ItemCollection to generate its content according to its ItemTemplate.
///
///
/// When first created, ItemCollection is in an uninitialized state, neither
/// ItemsSource-mode nor direct-mode. It will hold settings like SortDescriptions and Filter
/// until the mode is determined, then assign the settings to the active view.
/// When uninitialized, calls to the list-modifying members will put the
/// ItemCollection in direct mode, and setting the ItemsSource will put the
/// ItemCollection in ItemsSource mode.
///
[Localizability(LocalizationCategory.Ignore)]
public sealed class ItemCollection : CollectionView, IList, IWeakEventListener
{
//-----------------------------------------------------
//
// Constructors
//
//-----------------------------------------------------
#region Constructors
// ItemCollection cannot be created standalone, it is created by ItemsControl
///
/// Initializes a new instance of ItemCollection that is empty and has default initial capacity.
///
/// model parent of this item collection
///
///
internal ItemCollection(DependencyObject modelParent)
: base(EmptyEnumerable.Instance, false)
{
_modelParent = new WeakReference(modelParent);
}
///
/// Initializes a new instance of ItemCollection that is empty and has specified initial capacity.
///
/// model parent of this item collection
/// The number of items that the new list is initially capable of storing
///
/// Some ItemsControl implementations have better idea how many items to anticipate,
/// capacity parameter lets them tailor the initial size.
///
internal ItemCollection(FrameworkElement modelParent, int capacity)
: base(EmptyEnumerable.Instance, false)
{
_defaultCapacity = capacity;
_modelParent = new WeakReference(modelParent);
}
#endregion Constructors
//------------------------------------------------------
//
// Public Methods
//
//-----------------------------------------------------
#region Public Methods
//------------------------------------------------------
#region ICurrentItem
// These currency methods do not call OKToChangeCurrent() because
// ItemCollection already picks up and forwards the CurrentChanging
// event from the inner _collectionView.
///
/// Move to the first item.
///
/// true if points to an item within the view.
public override bool MoveCurrentToFirst()
{
if (!EnsureCollectionView())
return false;
VerifyRefreshNotDeferred();
return _collectionView.MoveCurrentToFirst();
}
///
/// Move to the next item.
///
/// true if points to an item within the view.
public override bool MoveCurrentToNext()
{
if (!EnsureCollectionView())
return false;
VerifyRefreshNotDeferred();
return _collectionView.MoveCurrentToNext();
}
///
/// Move to the previous item.
///
/// true if points to an item within the view.
public override bool MoveCurrentToPrevious()
{
if (!EnsureCollectionView())
return false;
VerifyRefreshNotDeferred();
return _collectionView.MoveCurrentToPrevious();
}
///
/// Move to the last item.
///
/// true if points to an item within the view.
public override bool MoveCurrentToLast()
{
if (!EnsureCollectionView())
return false;
VerifyRefreshNotDeferred();
return _collectionView.MoveCurrentToLast();
}
///
/// Move to the given item.
///
/// Move CurrentItem to this item.
/// true if points to an item within the view.
public override bool MoveCurrentTo(object item)
{
if (!EnsureCollectionView())
return false;
VerifyRefreshNotDeferred();
return _collectionView.MoveCurrentTo(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 (!EnsureCollectionView())
return false;
VerifyRefreshNotDeferred();
return _collectionView.MoveCurrentToPosition(position);
}
#endregion ICurrentItem
#region IList
///
/// Returns an enumerator object for this ItemCollection
///
///
/// Enumerator object for this ItemCollection
///
protected override IEnumerator GetEnumerator()
{
if (!EnsureCollectionView())
return EmptyEnumerator.Instance;
return ((IEnumerable)_collectionView).GetEnumerator();
}
///
/// Add an item to this collection.
///
///
/// New item to be added to collection
///
///
/// Zero-based index where the new item is added. -1 if the item could not be added.
///
///
/// To facilitate initialization of direct-mode ItemsControls with Sort and/or Filter,
/// Add() is permitted when ItemsControl is initializing, even if a Sort or Filter has been set.
///
///
/// trying to add an item which already has a different model/logical parent
/// - or -
/// trying to add an item when the ItemCollection is in ItemsSource mode.
///
public int Add(object newItem)
{
CheckIsUsingInnerView();
int index = _internalView.Add(newItem);
ModelParent.SetValue(ItemsControl.HasItemsPropertyKey, BooleanBoxes.TrueBox);
return index;
}
///
/// 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()
{
// Not using CheckIsUsingInnerView() because we don't want to create internal list
VerifyRefreshNotDeferred();
if (IsUsingItemsSource)
{
throw new InvalidOperationException(SR.Get(SRID.ItemsSourceInUse));
}
if (_internalView != null)
{
_internalView.Clear();
}
ModelParent.ClearValue(ItemsControl.HasItemsPropertyKey);
}
///
/// Checks to see if a given item is in this collection and in the view
///
///
/// The item whose membership in this collection is to be checked.
///
///
/// True if the collection contains the given item and the item passes the active filter
///
public override bool Contains(object containItem)
{
if (!EnsureCollectionView())
return false;
VerifyRefreshNotDeferred();
return _collectionView.Contains(containItem);
}
///
/// Makes a shallow copy of object references from this
/// ItemCollection to the given target array
///
///
/// Target of the copy operation
///
///
/// Zero-based index at which the copy begins
///
public void CopyTo(Array array, int index)
{
if (array == null)
throw new ArgumentNullException("array");
if (array.Rank > 1)
throw new ArgumentException(SR.Get(SRID.BadTargetArray), "array"); // array is multidimensional.
if (index < 0)
throw new ArgumentOutOfRangeException("index");
// use the view instead of the collection, because it may have special sort/filter
if (!EnsureCollectionView())
return; // there is no collection (bind returned no collection) and therefore nothing to copy
VerifyRefreshNotDeferred();
IndexedEnumerable.CopyTo(_collectionView, array, index);
}
///
/// Finds the index in this collection/view where the given item is found.
///
///
/// The item whose index in this collection/view is to be retrieved.
///
///
/// Zero-based index into the collection/view where the given item can be
/// found. Otherwise, -1
///
public override int IndexOf(object item)
{
if (!EnsureCollectionView())
return -1;
VerifyRefreshNotDeferred();
return _collectionView.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)
{
// only check lower bound because Count could be expensive
if (index < 0)
throw new ArgumentOutOfRangeException("index");
VerifyRefreshNotDeferred();
if (!EnsureCollectionView())
throw new InvalidOperationException(SR.Get(SRID.ItemCollectionHasNoCollection));
if (_collectionView == _internalView)
{
// check upper bound here because we know it's not expensive
if (index >= _internalView.Count)
throw new ArgumentOutOfRangeException("index");
}
return _collectionView.GetItemAt(index);
}
///
/// Insert an item in the collection at a given index. All items
/// after the given position are moved down by one.
///
///
/// The index at which to inser the item
///
///
/// The item reference to be added to the collection
///
///
/// Thrown when trying to add an item which already has a different model/logical parent
/// or when the ItemCollection is read-only because it is in ItemsSource mode
///
///
/// Thrown if index is out of range
///
public void Insert(int insertIndex, object insertItem)
{
CheckIsUsingInnerView();
_internalView.Insert(insertIndex, insertItem);
ModelParent.SetValue(ItemsControl.HasItemsPropertyKey, BooleanBoxes.TrueBox);
}
///
/// Removes the given item reference from the collection or view.
/// All remaining items move up by one.
///
///
/// the ItemCollection is read-only because it is in ItemsSource mode or there
/// is a sort or filter in effect
///
///
/// The item to be removed.
///
public void Remove(object removeItem)
{
CheckIsUsingInnerView();
_internalView.Remove(removeItem);
if (IsEmpty)
{
ModelParent.ClearValue(ItemsControl.HasItemsPropertyKey);
}
}
///
/// Removes an item from the collection or view at the given index.
/// All remaining items move up by one.
///
///
/// The index at which to remove an item.
///
///
/// the ItemCollection is read-only because it is in ItemsSource mode
///
///
/// Thrown if index is out of range
///
public void RemoveAt(int removeIndex)
{
CheckIsUsingInnerView();
_internalView.RemoveAt(removeIndex);
if (IsEmpty)
{
ModelParent.ClearValue(ItemsControl.HasItemsPropertyKey);
}
}
#endregion IList
///
/// Return true if the item is acceptable to the active filter, if any.
/// It is commonly used during collection-changed notifications to
/// determine if the added/removed item requires processing.
///
///
/// true if the item passes the filter or if no filter is set on collection view.
///
public override bool PassesFilter(object item)
{
if (!EnsureCollectionView())
return true;
return _collectionView.PassesFilter(item);
}
///
/// Re-create the view, using any and/or .
///
protected override void RefreshOverride()
{
if (_collectionView != null)
{
if (_collectionView.NeedsRefresh)
{
_collectionView.Refresh();
}
else
{
// if the view is up to date, we only need to raise the Reset event
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
}
#endregion Public Methods
//------------------------------------------------------
//
// Public Properties
//
//-----------------------------------------------------
#region Public Properties
///
/// Read-only property for the number of items stored in this collection of objects
///
///
/// returns 0 if the ItemCollection is uninitialized or
/// there is no collection in ItemsSource mode
///
public override int Count
{
get
{
if (!EnsureCollectionView())
return 0;
VerifyRefreshNotDeferred();
return _collectionView.Count;
}
}
///
/// Returns true if the resulting (filtered) view is emtpy.
///
public override bool IsEmpty
{
get
{
if (!EnsureCollectionView())
return true;
VerifyRefreshNotDeferred();
return _collectionView.IsEmpty;
}
}
///
/// Indexer property to retrieve or replace the item at the given
/// zero-based offset into the collection.
///
///
/// trying to set an item which already has a different model/logical parent; or,
/// trying to set when in ItemsSource mode; or,
/// the ItemCollection is uninitialized; or,
/// in ItemsSource mode, the binding on ItemsSource does not provide a collection.
///
///
/// Thrown if index is out of range
///
public object this[int index]
{
get
{
return GetItemAt(index);
}
set
{
CheckIsUsingInnerView();
if (index < 0 || index >= _internalView.Count)
throw new ArgumentOutOfRangeException("index");
_internalView[index] = value;
}
}
///
/// The ItemCollection's underlying collection or the user provided ItemsSource collection
///
public override IEnumerable SourceCollection
{
get
{
if (IsUsingItemsSource)
{
return ItemsSource;
}
else
{
EnsureInternalView();
return this;
}
}
}
///
/// Returns true if this view needs to be refreshed
/// (i.e. when the view is not consistent with the current sort or filter).
///
///
/// true when SortDescriptions or Filter is changed while refresh is deferred,
/// or in direct-mode, when an item have been added while SortDescriptions or Filter is in place.
///
public override bool NeedsRefresh
{
get
{
return (EnsureCollectionView()) ? _collectionView.NeedsRefresh : false;
}
}
///
/// Collection of Sort criteria to sort items in ItemCollection.
///
///
///
/// Sorting is supported for items in the ItemsControl.Items collection;
/// if a collection is assigned to ItemsControl.ItemsSource, the capability to sort
/// depends on the CollectionView for that inner collection.
/// Simpler implementations of CollectionVIew 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
{
// always hand out this ItemCollection's SortDescription collection;
// in ItemsSource mode the inner collection view will be kept in synch with this collection
if (_sort == null)
{
_sort = new SortDescriptionCollection();
if (_collectionView != null)
{
// no need to do this under the monitor - we haven't hooked up events yet
CloneList(_sort, _collectionView.SortDescriptions);
}
((INotifyCollectionChanged)_sort).CollectionChanged += new NotifyCollectionChangedEventHandler(SortDescriptionsChanged);
}
return _sort;
}
}
///
/// Test if this ICollectionView supports sorting before adding
/// to .
///
public override bool CanSort
{
get
{
return (EnsureCollectionView()) ? _collectionView.CanSort : true;
}
}
///
/// 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 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 filtering is supported before assigning
/// a non-null Filter value.
///
public override Predicate