/// <summary> /// Remove an item from the direct children of a group. /// </summary> /// <param name="group">Group to remove item from</param> /// <param name="item">Item to remove</param> /// <returns>True if item could not be removed</returns> private bool RemoveFromGroupDirectly(CollectionViewGroupInternal group, object item) { int leafIndex = group.Remove(item, true); if (leafIndex >= 0) { this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, leafIndex)); return(false); } else { return(true); } }
//------------------------------------------------------ // // Private Methods // //------------------------------------------------------ /// <summary> /// Removes an empty group from the PagedCollectionView grouping /// </summary> /// <param name="group">Empty subgroup to remove</param> private static void RemoveEmptyGroup(CollectionViewGroupInternal group) { CollectionViewGroupInternal parent = group.Parent; if (parent != null) { GroupDescription groupBy = parent.GroupBy; int index = parent.ProtectedItems.IndexOf(group); // remove the subgroup unless it is one of the explicit groups if (index >= groupBy.GroupNames.Count) { parent.Remove(group, false); } } }
/// <summary> /// Finds the index of the specified item /// </summary> /// <param name="item">Item we are looking for</param> /// <param name="seed">Seed of the item we are looking for</param> /// <param name="comparer">Comparer used to find the item</param> /// <param name="low">Low range of item index</param> /// <param name="high">High range of item index</param> /// <returns>Index of the specified item</returns> protected virtual int FindIndex(object item, object seed, IComparer comparer, int low, int high) { int index; if (comparer != null) { ListComparer listComparer = comparer as ListComparer; if (listComparer != null) { // reset the IListComparer before each search. This cannot be done // any less frequently (e.g. in Root.AddToSubgroups), due to the // possibility that the item may appear in more than one subgroup. listComparer.Reset(); } CollectionViewGroupComparer groupComparer = comparer as CollectionViewGroupComparer; if (groupComparer != null) { // reset the CollectionViewGroupComparer before each search. This cannot be done // any less frequently (e.g. in Root.AddToSubgroups), due to the // possibility that the item may appear in more than one subgroup. groupComparer.Reset(); } for (index = low; index < high; ++index) { CollectionViewGroupInternal subgroup = this.ProtectedItems[index] as CollectionViewGroupInternal; object seed1 = (subgroup != null) ? subgroup.SeedItem : this.ProtectedItems[index]; if (seed1 == DependencyProperty.UnsetValue) { continue; } if (comparer.Compare(seed, seed1) < 0) { break; } } } else { index = high; } return(index); }
/// <summary> /// The item did not appear in one or more of the subgroups it /// was supposed to. This can happen if the item's properties /// change so that the group names we used to insert it are /// different from the names used to remove it. If this happens, /// remove the item the hard way. /// </summary> /// <param name="group">Group to remove item from</param> /// <param name="item">Item to remove</param> private void RemoveItemFromSubgroupsByExhaustiveSearch(CollectionViewGroupInternal group, object item) { // try to remove the item from the direct children // this function only returns true if it failed to remove from group directly // in which case we will step through and search exhaustively if (this.RemoveFromGroupDirectly(group, item)) { // if that didn't work, recurse into each subgroup // (loop runs backwards in case an entire group is deleted) for (int k = group.Items.Count - 1; k >= 0; --k) { CollectionViewGroupInternal subgroup = group.Items[k] as CollectionViewGroupInternal; if (subgroup != null) { this.RemoveItemFromSubgroupsByExhaustiveSearch(subgroup, item); } } } }
//------------------------------------------------------ // // Private Methods // //------------------------------------------------------ /// <summary> /// Add an item to the subgroup with the given name /// </summary> /// <param name="item">Item to add</param> /// <param name="group">Group to add item to</param> /// <param name="level">The level of grouping.</param> /// <param name="name">Name of subgroup to add to</param> /// <param name="loading">Whether we are currently loading</param> private void AddToSubgroup(object item, CollectionViewGroupInternal group, int level, object name, bool loading) { CollectionViewGroupInternal subgroup; int index = (this._isDataInGroupOrder) ? group.LastIndex : 0; // find the desired subgroup for (int n = group.Items.Count; index < n; ++index) { subgroup = group.Items[index] as CollectionViewGroupInternal; if (subgroup == null) { continue; // skip children that are not groups } if (group.GroupBy.NamesMatch(subgroup.Name, name)) { group.LastIndex = index; this.AddToSubgroups(item, subgroup, level + 1, loading); return; } } // the item didn't match any subgroups. Create a new subgroup and add the item. subgroup = new CollectionViewGroupInternal(name, group); this.InitializeGroup(subgroup, level + 1, item); if (loading) { group.Add(subgroup); group.LastIndex = index; } else { // using insert will find the correct sort index to // place the subgroup, and will default to the last // position if no ActiveComparer is specified group.Insert(subgroup, item, this.ActiveComparer); } this.AddToSubgroups(item, subgroup, level + 1, loading); }
/// <summary> /// MoveNext implementation for IEnumerator /// </summary> /// <returns>Returns whether the MoveNext operation was successful</returns> bool IEnumerator.MoveNext() { Debug.Assert(this._group != null, "_group should have been initialized in constructor"); // check for invalidated enumerator if (this._group._version != this._version) { throw new InvalidOperationException(); } // move forward to the next leaf while (this._subEnum == null || !this._subEnum.MoveNext()) { // done with the current top-level item. Move to the next one. ++this._index; if (this._index >= this._group.Items.Count) { return(false); } CollectionViewGroupInternal subgroup = this._group.Items[this._index] as CollectionViewGroupInternal; if (subgroup == null) { // current item is a leaf - it's the new Current this._current = this._group.Items[this._index]; this._subEnum = null; return(true); } else { // current item is a subgroup - get its enumerator this._subEnum = subgroup.GetLeafEnumerator(); } } // the loop terminates only when we have a subgroup enumerator // positioned at the new Current item this._current = this._subEnum.Current; return(true); }
/// <summary> /// Initialize the given group /// </summary> /// <param name="group">Group to initialize</param> /// <param name="level">The level of grouping</param> /// <param name="seedItem">The seed item to compare with to see where to insert</param> private void InitializeGroup(CollectionViewGroupInternal group, int level, object seedItem) { // set the group description for dividing the group into subgroups GroupDescription groupDescription = this.GetGroupDescription(group, level); group.GroupBy = groupDescription; // create subgroups for each of the explicit names ObservableCollection <object> explicitNames = (groupDescription != null) ? groupDescription.GroupNames : null; if (explicitNames != null) { for (int k = 0, n = explicitNames.Count; k < n; ++k) { CollectionViewGroupInternal subgroup = new CollectionViewGroupInternal(explicitNames[k], group); this.InitializeGroup(subgroup, level + 1, seedItem); group.Add(subgroup); } } group.LastIndex = 0; }
/// <summary> /// Returns the index of the given item within the list of leaves governed /// by this group /// </summary> /// <param name="item">Item we are looking for</param> /// <returns>Number of items under that leaf</returns> internal int LeafIndexOf(object item) { int leaves = 0; // number of leaves we've passed over so far for (int k = 0, n = Items.Count; k < n; ++k) { CollectionViewGroupInternal subgroup = Items[k] as CollectionViewGroupInternal; if (subgroup != null) { int subgroupIndex = subgroup.LeafIndexOf(item); if (subgroupIndex < 0) { leaves += subgroup.ItemCount; // item not in this subgroup } else { return(leaves + subgroupIndex); // item is in this subgroup } } else { // current item is a leaf - compare it directly if (object.Equals(item, Items[k])) { return(leaves); } else { leaves += 1; } } } // item not found return(-1); }
/// <summary> /// Remove an item from the subgroup with the given name. /// </summary> /// <param name="item">Item to remove</param> /// <param name="group">Group to remove item from</param> /// <param name="level">The level of grouping</param> /// <param name="name">Name of item to remove</param> /// <returns>Return true if the item was not in one of the subgroups it was supposed to be.</returns> private bool RemoveFromSubgroup(object item, CollectionViewGroupInternal group, int level, object name) { bool itemIsMissing = false; CollectionViewGroupInternal subgroup; // find the desired subgroup for (int index = 0, n = group.Items.Count; index < n; ++index) { subgroup = group.Items[index] as CollectionViewGroupInternal; if (subgroup == null) { continue; // skip children that are not groups } if (group.GroupBy.NamesMatch(subgroup.Name, name)) { if (this.RemoveFromSubgroups(item, subgroup, level + 1)) { itemIsMissing = true; } return itemIsMissing; } } // the item didn't match any subgroups. It should have. return true; }
//------------------------------------------------------ // // Constructors // //------------------------------------------------------ /// <summary> /// Initializes a new instance of the CollectionViewGroupInternal class. /// </summary> /// <param name="name">Name of the CollectionViewGroupInternal</param> /// <param name="parent">Parent node of the CollectionViewGroup</param> internal CollectionViewGroupInternal(object name, CollectionViewGroupInternal parent) : base(name) { this._parentGroup = parent; }
private int _version; // parent group's version at ctor /// <summary> /// Initializes a new instance of the LeafEnumerator class. /// </summary> /// <param name="group">CollectionViewGroupInternal that uses the enumerator</param> public LeafEnumerator(CollectionViewGroupInternal group) { this._group = group; this.DoReset(); // don't call virtual Reset in ctor }
//------------------------------------------------------ // // Private Methods // //------------------------------------------------------ /// <summary> /// Removes an empty group from the PagedCollectionView grouping /// </summary> /// <param name="group">Empty subgroup to remove</param> private static void RemoveEmptyGroup(CollectionViewGroupInternal group) { CollectionViewGroupInternal parent = group.Parent; if (parent != null) { GroupDescription groupBy = parent.GroupBy; int index = parent.ProtectedItems.IndexOf(group); // remove the subgroup unless it is one of the explicit groups if (index >= groupBy.GroupNames.Count) { parent.Remove(group, false); } } }
//------------------------------------------------------ // // Constructors // //------------------------------------------------------ /// <summary> /// Initializes a new instance of the CollectionViewGroupInternal class. /// </summary> /// <param name="name">Name of the CollectionViewGroupInternal</param> /// <param name="parent">Parent node of the CollectionViewGroup</param> internal CollectionViewGroupInternal(object name, CollectionViewGroupInternal parent) : base(name) { this._parentGroup = parent; }
/// <summary> /// The item did not appear in one or more of the subgroups it /// was supposed to. This can happen if the item's properties /// change so that the group names we used to insert it are /// different from the names used to remove it. If this happens, /// remove the item the hard way. /// </summary> /// <param name="group">Group to remove item from</param> /// <param name="item">Item to remove</param> private void RemoveItemFromSubgroupsByExhaustiveSearch(CollectionViewGroupInternal group, object item) { // try to remove the item from the direct children // this function only returns true if it failed to remove from group directly // in which case we will step through and search exhaustively if (this.RemoveFromGroupDirectly(group, item)) { // if that didn't work, recurse into each subgroup // (loop runs backwards in case an entire group is deleted) for (int k = group.Items.Count - 1; k >= 0; --k) { CollectionViewGroupInternal subgroup = group.Items[k] as CollectionViewGroupInternal; if (subgroup != null) { this.RemoveItemFromSubgroupsByExhaustiveSearch(subgroup, item); } } } }
/// <summary> /// Remove an item from the desired subgroup(s) of the given group. /// </summary> /// <param name="item">Item to remove</param> /// <param name="group">Group to remove item from</param> /// <param name="level">The level of grouping</param> /// <returns>Return true if the item was not in one of the subgroups it was supposed to be.</returns> private bool RemoveFromSubgroups(object item, CollectionViewGroupInternal group, int level) { bool itemIsMissing = false; object name = this.GetGroupName(item, group.GroupBy, level); ICollection nameList; if (name == UseAsItemDirectly) { // the item belongs to the group itself (not to any subgroups) itemIsMissing = this.RemoveFromGroupDirectly(group, item); } else if ((nameList = name as ICollection) == null) { // the item belongs to one subgroup if (this.RemoveFromSubgroup(item, group, level, name)) { itemIsMissing = true; } } else { // the item belongs to multiple subgroups foreach (object o in nameList) { if (this.RemoveFromSubgroup(item, group, level, o)) { itemIsMissing = true; } } } return itemIsMissing; }
/// <summary> /// Remove an item from the direct children of a group. /// </summary> /// <param name="group">Group to remove item from</param> /// <param name="item">Item to remove</param> /// <returns>True if item could not be removed</returns> private bool RemoveFromGroupDirectly(CollectionViewGroupInternal group, object item) { int leafIndex = group.Remove(item, true); if (leafIndex >= 0) { this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, leafIndex)); return false; } else { return true; } }
/// <summary> /// Initialize the given group /// </summary> /// <param name="group">Group to initialize</param> /// <param name="level">The level of grouping</param> /// <param name="seedItem">The seed item to compare with to see where to insert</param> private void InitializeGroup(CollectionViewGroupInternal group, int level, object seedItem) { // set the group description for dividing the group into subgroups GroupDescription groupDescription = this.GetGroupDescription(group, level); group.GroupBy = groupDescription; // create subgroups for each of the explicit names ObservableCollection<object> explicitNames = (groupDescription != null) ? groupDescription.GroupNames : null; if (explicitNames != null) { for (int k = 0, n = explicitNames.Count; k < n; ++k) { CollectionViewGroupInternal subgroup = new CollectionViewGroupInternal(explicitNames[k], group); this.InitializeGroup(subgroup, level + 1, seedItem); group.Add(subgroup); } } group.LastIndex = 0; }
/// <summary> /// Add an item to the desired subgroup(s) of the given group /// </summary> /// <param name="item">Item to add</param> /// <param name="group">Group to add item to</param> /// <param name="level">The level of grouping</param> /// <param name="loading">Whether we are currently loading</param> private void AddToSubgroups(object item, CollectionViewGroupInternal group, int level, bool loading) { object name = this.GetGroupName(item, group.GroupBy, level); ICollection nameList; if (name == UseAsItemDirectly) { // the item belongs to the group itself (not to any subgroups) if (loading) { group.Add(item); } else { int localIndex = group.Insert(item, item, this.ActiveComparer); int index = group.LeafIndexFromItem(item, localIndex); this.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index)); } } else if ((nameList = name as ICollection) == null) { // the item belongs to one subgroup this.AddToSubgroup(item, group, level, name, loading); } else { // the item belongs to multiple subgroups foreach (object o in nameList) { this.AddToSubgroup(item, group, level, o, loading); } } }
//------------------------------------------------------ // // Private Methods // //------------------------------------------------------ /// <summary> /// Add an item to the subgroup with the given name /// </summary> /// <param name="item">Item to add</param> /// <param name="group">Group to add item to</param> /// <param name="level">The level of grouping.</param> /// <param name="name">Name of subgroup to add to</param> /// <param name="loading">Whether we are currently loading</param> private void AddToSubgroup(object item, CollectionViewGroupInternal group, int level, object name, bool loading) { CollectionViewGroupInternal subgroup; int index = (this._isDataInGroupOrder) ? group.LastIndex : 0; // find the desired subgroup for (int n = group.Items.Count; index < n; ++index) { subgroup = group.Items[index] as CollectionViewGroupInternal; if (subgroup == null) { continue; // skip children that are not groups } if (group.GroupBy.NamesMatch(subgroup.Name, name)) { group.LastIndex = index; this.AddToSubgroups(item, subgroup, level + 1, loading); return; } } // the item didn't match any subgroups. Create a new subgroup and add the item. subgroup = new CollectionViewGroupInternal(name, group); this.InitializeGroup(subgroup, level + 1, item); if (loading) { group.Add(subgroup); group.LastIndex = index; } else { // using insert will find the correct sort index to // place the subgroup, and will default to the last // position if no ActiveComparer is specified group.Insert(subgroup, item, this.ActiveComparer); } this.AddToSubgroups(item, subgroup, level + 1, loading); }
private int _version; // parent group's version at ctor /// <summary> /// Initializes a new instance of the LeafEnumerator class. /// </summary> /// <param name="group">CollectionViewGroupInternal that uses the enumerator</param> public LeafEnumerator(CollectionViewGroupInternal group) { this._group = group; this.DoReset(); // don't call virtual Reset in ctor }