//----------------------------------------------------------------------------
//
//
// Copyright (C) 2003 by Microsoft Corporation. All rights reserved.
//
//
//
// Description: Root of CollectionViewGroup structure, as created by a CollectionView according to a GroupDescription.
// CollectionView classes use this class to manage all Grouping functionality.
//
// See spec at http://avalon/connecteddata/Specs/Grouping.mht
//
//---------------------------------------------------------------------------
using System;
using System.Collections; // IComparer
using System.Collections.ObjectModel; // ObservableCollection
using System.Collections.Specialized; // INotifyCollectionChanged
using System.ComponentModel; // PropertyChangedEventArgs, GroupDescription
using System.Diagnostics; // Debug.Assert
using System.Globalization;
using System.Windows.Data; // CollectionViewGroup
namespace MS.Internal.Data
{
// CollectionView classes use this class as the manager of all Grouping functionality
internal class CollectionViewGroupRoot : CollectionViewGroupInternal, INotifyCollectionChanged
{
internal CollectionViewGroupRoot(CollectionView view) : base("Root", null)
{
_view = view;
}
#region INotifyCollectionChanged
///
/// Raise this event when the (grouped) view changes
///
public event NotifyCollectionChangedEventHandler CollectionChanged;
///
/// Notify listeners that this View has changed
///
///
/// CollectionViews (and sub-classes) should take their filter/sort/grouping
/// into account before calling this method to forward CollectionChanged events.
///
///
/// The NotifyCollectionChangedEventArgs to be passed to the EventHandler
///
public void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
{
if (args == null)
throw new ArgumentNullException("args");
if (CollectionChanged != null)
CollectionChanged(this, args);
}
#endregion INotifyCollectionChanged
///
/// The description of grouping, indexed by level.
///
public virtual ObservableCollection GroupDescriptions
{
get { return _groupBy; }
}
///
/// A delegate to select the group description as a function of the
/// parent group and its level.
///
public virtual GroupDescriptionSelectorCallback GroupBySelector
{
get { return _groupBySelector; }
set { _groupBySelector = value; }
}
// a group description has changed somewhere in the tree - notify host
protected override void OnGroupByChanged()
{
if (GroupDescriptionChanged != null)
GroupDescriptionChanged(this, EventArgs.Empty);
}
#region Internal Events and Properties
internal event EventHandler GroupDescriptionChanged;
internal IComparer ActiveComparer
{
get { return _comparer; }
set { _comparer = value; }
}
///
/// Culture to use during sorting.
///
internal CultureInfo Culture
{
get { return _view.Culture; }
}
internal bool IsDataInGroupOrder
{
get { return _isDataInGroupOrder; }
set { _isDataInGroupOrder = value; }
}
#endregion Internal Events and Properties
#region Internal Methods
internal void Initialize()
{
if (_topLevelGroupDescription == null)
{
_topLevelGroupDescription = new TopLevelGroupDescription();
}
InitializeGroup(this, _topLevelGroupDescription, 0);
}
internal void AddToSubgroups(object item, bool loading)
{
AddToSubgroups(item, this, 0, loading);
}
internal bool RemoveFromSubgroups(object item)
{
return RemoveFromSubgroups(item, this, 0);
}
internal void RemoveItemFromSubgroupsByExhaustiveSearch(object item)
{
RemoveItemFromSubgroupsByExhaustiveSearch(this, item);
}
internal void InsertSpecialItem(int index, object item, bool loading)
{
ChangeCounts(item, +1);
ProtectedItems.Insert(index, item);
if (!loading)
{
int globalIndex = this.LeafIndexFromItem(item, index);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, globalIndex));
}
}
internal void RemoveSpecialItem(int index, object item, bool loading)
{
Debug.Assert(Object.Equals(item, ProtectedItems[index]), "RemoveSpecialItem finds inconsistent data");
int globalIndex = -1;
if (!loading)
{
globalIndex = this.LeafIndexFromItem(item, index);
}
ChangeCounts(item, -1);
ProtectedItems.RemoveAt(index);
if (!loading)
{
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, globalIndex));
}
}
protected override int FindIndex(object item, object seed, IComparer comparer, int low, int high)
{
// root group needs to adjust the bounds of the search to exclude the
// placeholder and new item (if any)
IEditableCollectionView iecv = _view as IEditableCollectionView;
if (iecv != null)
{
if (iecv.NewItemPlaceholderPosition == NewItemPlaceholderPosition.AtBeginning)
{
++low;
if (iecv.IsAddingNew)
{
++low;
}
}
else
{
if (iecv.IsAddingNew)
{
--high;
}
if (iecv.NewItemPlaceholderPosition == NewItemPlaceholderPosition.AtEnd)
{
--high;
}
}
}
return base.FindIndex(item, seed, comparer, low, high);
}
#endregion Internal Methods
#region private methods
// Initialize the given group
void InitializeGroup(CollectionViewGroupInternal group, GroupDescription parentDescription, int level)
{
// set the group description for dividing the group into subgroups
GroupDescription groupDescription = GetGroupDescription(group, parentDescription, level);
group.GroupBy = groupDescription;
// create subgroups for each of the explicit names
ObservableCollection