File: Base\System\ComponentModel\GroupDescription.cs
Project: wpf\src\WindowsBase.csproj (WindowsBase)
//---------------------------------------------------------------------------
//
// <copyright file="GroupDescription.cs" company="Microsoft">
//    Copyright (C) 2003 by Microsoft Corporation.  All rights reserved.
// </copyright>
//
//
// Description: Base class for group descriptions.
//
// See spec at http://avalon/connecteddata/Specs/Grouping.mht
//
//---------------------------------------------------------------------------
 
using System.Collections;               // IComparer
using System.Collections.ObjectModel;   // ObservableCollection
using System.Collections.Specialized;   // NotifyCollectionChangedEvent*
using System.Globalization;             // CultureInfo
using MS.Internal;                      // Invariant.Assert
 
namespace System.ComponentModel
{
    /// <summary>
    /// Base class for group descriptions.
    /// A GroupDescription describes how to divide the items in a collection
    /// into groups.
    /// </summary>
    public abstract class GroupDescription : INotifyPropertyChanged
    {
        #region Constructors
 
        //------------------------------------------------------
        //
        //  Constructors
        //
        //------------------------------------------------------
 
        /// <summary>
        /// Initializes a new instance of GroupDescription.
        /// </summary>
        protected GroupDescription()
        {
            _explicitGroupNames = new ObservableCollection<object>();
            _explicitGroupNames.CollectionChanged += new NotifyCollectionChangedEventHandler(OnGroupNamesChanged);
        }
 
        #endregion Constructors
 
        #region INotifyPropertyChanged
 
        /// <summary>
        ///     This event is raised when a property of the group description has changed.
        /// </summary>
        event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
        {
            add
            {
                PropertyChanged += value;
            }
            remove
            {
                PropertyChanged -= value;
            }
        }
 
        /// <summary>
        /// PropertyChanged event (per <see cref="INotifyPropertyChanged" />).
        /// </summary>
        protected virtual event PropertyChangedEventHandler PropertyChanged;
 
        /// <summary>
        /// A subclass can call this method to raise the PropertyChanged event.
        /// </summary>
        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, e);
            }
        }
 
        #endregion INotifyPropertyChanged
 
        #region Public Properties
 
        //------------------------------------------------------
        //
        //  Public Properties
        //
        //------------------------------------------------------
 
        /// <summary>
        /// This list of names is used to initialize a group with a set of
        /// subgroups with the given names.  (Additional subgroups may be
        /// added later, if there are items that don't match any of the names.)
        /// </summary>
        public ObservableCollection<object> GroupNames
        {
            get { return _explicitGroupNames; }
        }
 
        /// <summary>
        /// This method is used by TypeDescriptor to determine if this property should
        /// be serialized.
        /// </summary>
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeGroupNames()
        {
            return (_explicitGroupNames.Count > 0);
        }
 
        /// <summary>
        /// Collection of Sort criteria to sort the groups.
        /// </summary>
        public SortDescriptionCollection SortDescriptions
        {
            get
            {
                if (_sort == null)
                    SetSortDescriptions(new SortDescriptionCollection());
                return _sort;
            }
        }
 
        /// <summary>
        /// This method is used by TypeDescriptor to determine if this property should
        /// be serialized.
        /// </summary>
        [EditorBrowsable(EditorBrowsableState.Never)]
        public bool ShouldSerializeSortDescriptions()
        {
            return (_sort != null && _sort.Count > 0);
        }
 
        /// <summary>
        /// Set a custom comparer to sort groups using an object that implements IComparer.
        /// </summary>
        /// <remarks>
        /// Note: Setting the custom comparer object will clear previously set <seealso cref="GroupDescription.SortDescriptions"/>.
        /// </remarks>
        public IComparer CustomSort
        {
            get { return _customSort; }
            set
            {
                _customSort = value;
                SetSortDescriptions(null);
                OnPropertyChanged(new PropertyChangedEventArgs("CustomSort"));
            }
        }
 
        #endregion Public Properties
 
        #region Public Methods
 
        //------------------------------------------------------
        //
        //  Public Methods
        //
        //------------------------------------------------------
 
        /// <summary>
        /// Return the group name(s) for the given item
        /// </summary>
        public abstract object GroupNameFromItem(object item, int level, CultureInfo culture);
 
        /// <summary>
        /// Return true if the names match (i.e the item should belong to the group).
        /// </summary>
        public virtual bool NamesMatch(object groupName, object itemName)
        {
            return Object.Equals(groupName, itemName);
        }
 
        #endregion Public Methods
 
        #region Internal Properties
 
        //------------------------------------------------------
        //
        //  Internal Properties
        //
        //------------------------------------------------------
 
        /// <summary>
        /// Collection of Sort criteria to sort the groups.  Does not do lazy initialization.
        /// </summary>
        internal SortDescriptionCollection SortDescriptionsInternal
        {
            get { return _sort; }
        }
 
        #endregion Internal Properties
 
        #region Private Methods
 
        //------------------------------------------------------
        //
        //  Private Methods
        //
        //------------------------------------------------------
 
        void OnGroupNamesChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            OnPropertyChanged(new PropertyChangedEventArgs("GroupNames"));
        }
 
        // set new SortDescription collection; rehook collection change notification handler
        private void SetSortDescriptions(SortDescriptionCollection descriptions)
        {
            if (_sort != null)
            {
                ((INotifyCollectionChanged)_sort).CollectionChanged -= new NotifyCollectionChangedEventHandler(SortDescriptionsChanged);
            }
 
            bool raiseChangeEvent = (_sort != descriptions);
 
            _sort = descriptions;
 
            if (_sort != null)
            {
                Invariant.Assert(_sort.Count == 0, "must be empty SortDescription collection");
                ((INotifyCollectionChanged)_sort).CollectionChanged += new NotifyCollectionChangedEventHandler(SortDescriptionsChanged);
            }
 
            if (raiseChangeEvent)
            {
                OnPropertyChanged(new PropertyChangedEventArgs("SortDescriptions"));
            }
        }
 
        // SortDescription was added/removed, notify listeners
        private void SortDescriptionsChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            // adding to SortDescriptions overrides custom sort
            if (_sort.Count > 0)
            {
                if (_customSort != null)
                {
                    _customSort = null;
                    OnPropertyChanged(new PropertyChangedEventArgs("CustomSort"));
                }
            }
 
            OnPropertyChanged(new PropertyChangedEventArgs("SortDescriptions"));
        }
 
 
        #endregion Private Methods
 
        #region Private fields
 
        //------------------------------------------------------
        //
        //  Private fields
        //
        //------------------------------------------------------
 
        ObservableCollection<object> _explicitGroupNames;
        SortDescriptionCollection _sort;
        IComparer _customSort;
 
        #endregion Private fields
    }
}