//----------------------------------------------------------------------------
//
//
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//
// Description: Defines CollectionViewSource object, the markup-accessible entry
// point to CollectionView.
//
// See spec at http://avalon/connecteddata/Specs/CollectionViewSource.mht
//
//---------------------------------------------------------------------------
using System;
using System.Collections; // IEnumerable
using System.Collections.ObjectModel; // ObservableCollection
using System.Collections.Specialized; // NotifyCollectionChanged*
using System.Globalization; // CultureInfo
using System.ComponentModel; // ICollectionView
using System.Windows.Markup; // XmlLanguage
using MS.Internal; // Invariant.Assert
using MS.Internal.Data; // DataBindEngine
namespace System.Windows.Data
{
///
/// Describes a collection view.
///
public class CollectionViewSource : DependencyObject, ISupportInitialize, IWeakEventListener
{
#region Constructors
//
// Constructors
//
///
/// Initializes a new instance of the CollectionViewSource class.
///
public CollectionViewSource()
{
_sort = new SortDescriptionCollection();
((INotifyCollectionChanged)_sort).CollectionChanged += new NotifyCollectionChangedEventHandler(OnForwardedCollectionChanged);
_groupBy = new ObservableCollection();
((INotifyCollectionChanged)_groupBy).CollectionChanged += new NotifyCollectionChangedEventHandler(OnForwardedCollectionChanged);
}
#endregion Constructors
#region Public Properties
//
// Public Properties
//
///
/// The key needed to define a read-only property.
///
private static readonly DependencyPropertyKey ViewPropertyKey
= DependencyProperty.RegisterReadOnly(
"View",
typeof(ICollectionView),
typeof(CollectionViewSource),
new FrameworkPropertyMetadata((ICollectionView)null));
///
/// The DependencyProperty for the View property.
/// Flags: None
/// Other: Read-Only
/// Default Value: null
///
public static readonly DependencyProperty ViewProperty
= ViewPropertyKey.DependencyProperty;
///
/// Returns the ICollectionView currently affiliated with this CollectionViewSource.
///
[ReadOnly(true)]
public ICollectionView View
{
get
{
return GetOriginalView(CollectionView);
}
}
///
/// The DependencyProperty for the Source property.
/// Flags: none
/// Default Value: null
///
public static readonly DependencyProperty SourceProperty
= DependencyProperty.Register(
"Source",
typeof(object),
typeof(CollectionViewSource),
new FrameworkPropertyMetadata(
(object)null,
new PropertyChangedCallback(OnSourceChanged)),
new ValidateValueCallback(IsSourceValid));
///
/// Source is the underlying collection.
///
public object Source
{
get { return (object) GetValue(SourceProperty); }
set { SetValue(SourceProperty, value); }
}
///
/// Called when SourceProperty is invalidated on "d."
///
/// The object on which the property was invalidated.
/// Argument.
private static void OnSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CollectionViewSource ctrl = (CollectionViewSource) d;
ctrl.OnSourceChanged(e.OldValue, e.NewValue);
ctrl.EnsureView();
}
///
/// This method is invoked when the Source property changes.
///
/// The old value of the Source property.
/// The new value of the Source property.
protected virtual void OnSourceChanged(object oldSource, object newSource)
{
}
private static bool IsSourceValid(object o)
{
return (o == null ||
o is IEnumerable ||
o is IListSource ||
o is DataSourceProvider) &&
!(o is ICollectionView);
}
private static bool IsValidSourceForView(object o)
{
return (o == null ||
o is IEnumerable ||
o is IListSource);
}
///
/// The DependencyProperty for the CollectionViewType property.
/// Flags: none
/// Default Value: null
///
public static readonly DependencyProperty CollectionViewTypeProperty
= DependencyProperty.Register(
"CollectionViewType",
typeof(Type),
typeof(CollectionViewSource),
new FrameworkPropertyMetadata(
(Type)null,
new PropertyChangedCallback(OnCollectionViewTypeChanged)),
new ValidateValueCallback(IsCollectionViewTypeValid));
///
/// CollectionViewType is the desired type of the View.
///
///
/// This property may only be set during initialization.
///
public Type CollectionViewType
{
get { return (Type) GetValue(CollectionViewTypeProperty); }
set { SetValue(CollectionViewTypeProperty, value); }
}
private static void OnCollectionViewTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CollectionViewSource ctrl = (CollectionViewSource) d;
Type oldCollectionViewType = (Type) e.OldValue;
Type newCollectionViewType = (Type) e.NewValue;
if (!ctrl._isInitializing)
throw new InvalidOperationException(SR.Get(SRID.CollectionViewTypeIsInitOnly));
ctrl.OnCollectionViewTypeChanged(oldCollectionViewType, newCollectionViewType);
ctrl.EnsureView();
}
///
/// This method is invoked when the CollectionViewType property changes.
///
/// The old value of the CollectionViewType property.
/// The new value of the CollectionViewType property.
protected virtual void OnCollectionViewTypeChanged(Type oldCollectionViewType, Type newCollectionViewType)
{
}
private static bool IsCollectionViewTypeValid(object o)
{
Type type = (Type)o;
return type == null ||
typeof(ICollectionView).IsAssignableFrom(type);
}
///
/// CultureInfo used for sorting, comparisons, etc.
/// This property is forwarded to any collection view created from this source.
///
[TypeConverter(typeof(System.Windows.CultureInfoIetfLanguageTagConverter))]
public CultureInfo Culture
{
get { return _culture; }
set { _culture = value; OnForwardedPropertyChanged(); }
}
///
/// Collection of SortDescriptions, describing sorting.
/// This property is forwarded to any collection view created from this source.
///
public SortDescriptionCollection SortDescriptions
{
get { return _sort; }
}
///
/// Collection of GroupDescriptions, describing grouping.
/// This property is forwarded to any collection view created from this source.
///
public ObservableCollection GroupDescriptions
{
get { return _groupBy; }
}
#endregion Public Properties
#region Public Events
///
/// An event requesting a filter query.
///
public event FilterEventHandler Filter
{
add
{
// Get existing event hanlders
FilterEventHandler handlers = FilterHandlersField.GetValue(this);
if (handlers != null)
{
// combine to a multicast delegate
handlers = (FilterEventHandler)Delegate.Combine(handlers, value);
}
else
{
handlers = value;
}
// Set the delegate as an uncommon field
FilterHandlersField.SetValue(this, handlers);
OnForwardedPropertyChanged();
}
remove
{
// Get existing event hanlders
FilterEventHandler handlers = FilterHandlersField.GetValue(this);
if (handlers != null)
{
// Remove the given handler
handlers = (FilterEventHandler)Delegate.Remove(handlers, value);
if (handlers == null)
{
// Clear the value for the uncommon field
// cause there are no more handlers
FilterHandlersField.ClearValue(this);
}
else
{
// Set the remaining handlers as an uncommon field
FilterHandlersField.SetValue(this, handlers);
}
}
OnForwardedPropertyChanged();
}
}
#endregion Public Events
#region Public Methods
//
// Public Methods
//
///
/// Return the default view for the given source. This view is never
/// affiliated with any CollectionViewSource.
///
public static ICollectionView GetDefaultView(object source)
{
return GetOriginalView(GetDefaultCollectionView(source, true));
}
// a version of the previous method that doesn't create the view (bug 108595)
private static ICollectionView LazyGetDefaultView(object source)
{
return GetOriginalView(GetDefaultCollectionView(source, false));
}
///
/// Return true if the given view is the default view for its source.
///
public static bool IsDefaultView(ICollectionView view)
{
if (view != null)
{
object source = view.SourceCollection;
return (GetOriginalView(view) == LazyGetDefaultView(source));
}
else
{
return true;
}
}
///
/// Enter a Defer Cycle.
/// Defer cycles are used to coalesce changes to the ICollectionView.
///
public IDisposable DeferRefresh()
{
return new DeferHelper(this);
}
#endregion Public Methods
//
// Interfaces
//
#region ISupportInitialize
/// Signals the object that initialization is starting.
void ISupportInitialize.BeginInit()
{
_isInitializing = true;
}
/// Signals the object that initialization is complete.
void ISupportInitialize.EndInit()
{
_isInitializing = false;
EnsureView();
}
#endregion ISupportInitialize
#region IWeakEventListener
///
/// Handle events from the centralized event table
///
bool IWeakEventListener.ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
return ReceiveWeakEvent(managerType, sender, e);
}
///
/// Handle events from the centralized event table
///
protected virtual bool ReceiveWeakEvent(Type managerType, object sender, EventArgs e)
{
if (managerType == typeof(DataChangedEventManager))
{
EnsureView();
}
else
{
return false; // unrecognized event
}
return true;
}
#endregion IWeakEventListener
#region Internal Properties
//
// Internal Properties
//
// Returns the CollectionView currently affiliate with this CollectionViewSource.
// This may be a CollectionViewProxy over the original view.
internal CollectionView CollectionView
{
get
{
ICollectionView view = (ICollectionView)GetValue(ViewProperty);
if (view != null && !_isViewInitialized)
{
// leak prevention: re-fetch ViewRecord instead of keeping a reference to it,
// to be sure that we don't inadvertently keep it alive.
object source = Source;
DataSourceProvider dataProvider = source as DataSourceProvider;
// if the source is DataSourceProvider, use its Data instead
if (dataProvider != null)
{
source = dataProvider.Data;
}
if (source != null)
{
DataBindEngine engine = DataBindEngine.CurrentDataBindEngine;
ViewRecord viewRecord = engine.GetViewRecord(source, this, CollectionViewType, true);
if (viewRecord != null)
{
viewRecord.InitializeView();
_isViewInitialized = true;
}
}
}
return (CollectionView)view;
}
}
// Returns the property through which inheritance context was established
internal DependencyProperty PropertyForInheritanceContext
{
get { return _propertyForInheritanceContext; }
}
#endregion Internal Properties
#region Internal Methods
//
// Internal Methods
//
// Return the default view for the given source. This view is never
// affiliated with any CollectionViewSource. It may be a
// CollectionViewProxy over the original view
static internal CollectionView GetDefaultCollectionView(object source, bool createView)
{
if (!IsValidSourceForView(source))
return null;
DataBindEngine engine = DataBindEngine.CurrentDataBindEngine;
ViewRecord viewRecord = engine.GetViewRecord(source, DefaultSource, null, createView);
return (viewRecord != null) ? (CollectionView)viewRecord.View : null;
}
///
/// Return the default view for the given source. This view is never
/// affiliated with any CollectionViewSource. The internal version sets
/// the culture on the view from the xml:Lang of the host object.
///
internal static CollectionView GetDefaultCollectionView(object source, DependencyObject d)
{
CollectionView view = GetDefaultCollectionView(source, true);
// at first use of a view, set its culture from the xml:lang of the
// element that's using the view
if (view != null && view.Culture == null)
{
XmlLanguage language = (d != null) ? (XmlLanguage)d.GetValue(FrameworkElement.LanguageProperty) : null;
if (language != null)
{
try
{
view.Culture = language.GetSpecificCulture();
}
catch (InvalidOperationException)
{
}
}
}
return view;
}
// Define the DO's inheritance context
internal override DependencyObject InheritanceContext
{
get { return _inheritanceContext; }
}
// Receive a new inheritance context (this will be a FE/FCE)
internal override void AddInheritanceContext(DependencyObject context, DependencyProperty property)
{
// remember which property caused the context - BindingExpression wants to know
// (this must happen before calling AddInheritanceContext, so that the answer
// is ready during the InheritanceContextChanged event)
if (!_hasMultipleInheritanceContexts && _inheritanceContext == null)
{
_propertyForInheritanceContext = property;
}
else
{
_propertyForInheritanceContext = null;
}
InheritanceContextHelper.AddInheritanceContext(context,
this,
ref _hasMultipleInheritanceContexts,
ref _inheritanceContext );
}
// Remove an inheritance context (this will be a FE/FCE)
internal override void RemoveInheritanceContext(DependencyObject context, DependencyProperty property)
{
InheritanceContextHelper.RemoveInheritanceContext(context,
this,
ref _hasMultipleInheritanceContexts,
ref _inheritanceContext);
// after removing a context, we don't know which property caused it
_propertyForInheritanceContext = null;
}
// Says if the current instance has multiple InheritanceContexts
internal override bool HasMultipleInheritanceContexts
{
get { return _hasMultipleInheritanceContexts; }
}
//
internal bool IsShareableInTemplate()
{
return false;
}
#endregion Internal Methods
#region Private Methods
//
// Private Methods
//
// Obtain the view affiliated with the current source. This may create
// a new view, or re-use an existing one.
void EnsureView()
{
EnsureView(Source, CollectionViewType);
}
void EnsureView(object source, Type collectionViewType)
{
if (_isInitializing || _deferLevel > 0)
return;
ICollectionView view;
DataSourceProvider dataProvider = source as DataSourceProvider;
// listen for DataChanged events from an DataSourceProvider
if (dataProvider != _dataProvider)
{
if (_dataProvider != null)
{
DataChangedEventManager.RemoveListener(_dataProvider, this);
}
_dataProvider = dataProvider;
if (_dataProvider != null)
{
DataChangedEventManager.AddListener(_dataProvider, this);
_dataProvider.InitialLoad();
}
}
// if the source is DataSourceProvider, use its Data instead
if (dataProvider != null)
{
source = dataProvider.Data;
}
// get the view
if (source != null)
{
DataBindEngine engine = DataBindEngine.CurrentDataBindEngine;
ViewRecord viewRecord = engine.GetViewRecord(source, this, collectionViewType, true);
view = viewRecord.View;
_isViewInitialized = viewRecord.IsInitialized;
// bring view up to date with the CollectionViewSource
if (_version != viewRecord.Version)
{
ApplyPropertiesToView(view);
viewRecord.Version = _version;
}
}
else
{
view = null;
}
// update the View property
SetValue(ViewPropertyKey, view);
}
// Forward properties from the CollectionViewSource to the CollectionView
void ApplyPropertiesToView(ICollectionView view)
{
if (view == null || _deferLevel > 0)
return;
using (view.DeferRefresh())
{
int i, n;
// Culture
if (Culture != null)
{
view.Culture = Culture;
}
// Sort
if (view.CanSort)
{
view.SortDescriptions.Clear();
for (i=0, n=SortDescriptions.Count; i < n; ++i)
{
view.SortDescriptions.Add(SortDescriptions[i]);
}
}
else if (SortDescriptions.Count > 0)
throw new InvalidOperationException(SR.Get(SRID.CannotSortView, view));
// Filter
Predicate