internal void RemoveAllInternal(bool saveRecycleQueue) { // Take _itemMap offline, to protect against reentrancy (bug 1285179) ItemBlock itemMap = _itemMap; _itemMap = null; try { // de-initialize the containers that are being removed if (itemMap != null) { for (ItemBlock block = itemMap.Next; block != itemMap; block = block.Next) { RealizedItemBlock rib = block as RealizedItemBlock; if (rib != null) { for (int offset = 0; offset < rib.ContainerCount; ++offset) { UnlinkContainerFromItem(rib.ContainerAt(offset), rib.ItemAt(offset)); } } } } } finally { PrepareGrouping(); // re-initialize the data structure _itemMap = new ItemBlock(); _itemMap.Prev = _itemMap.Next = _itemMap; UnrealizedItemBlock uib = new UnrealizedItemBlock(); uib.InsertAfter(_itemMap); uib.ItemCount = ItemsInternal.Count; if (!saveRecycleQueue) { ResetRecyclableContainers(); } SetAlternationCount(); // tell generators what happened if (MapChanged != null) { MapChanged(null, -1, 0, uib, 0, 0); } } }
/// <summary> /// Remove generated elements. /// </summary> private void Remove(GeneratorPosition position, int count, bool isRecycling) { if (position.Offset != 0) throw new ArgumentException(SR.Get(SRID.RemoveRequiresOffsetZero, position.Index, position.Offset), "position"); if (count <= 0) throw new ArgumentException(SR.Get(SRID.RemoveRequiresPositiveCount, count), "count"); if (_itemMap == null) { // ignore reentrant call (during RemoveAllInternal) Debug.Assert(false, "Unexpected reentrant call to ICG.Remove"); return; } int index = position.Index; ItemBlock block; // find the leftmost item to remove int offsetL = index; for (block = _itemMap.Next; block != _itemMap; block = block.Next) { if (offsetL < block.ContainerCount) break; offsetL -= block.ContainerCount; } RealizedItemBlock blockL = block as RealizedItemBlock; // find the rightmost item to remove int offsetR = offsetL + count - 1; for (; block != _itemMap; block = block.Next) { if (!(block is RealizedItemBlock)) throw new InvalidOperationException(SR.Get(SRID.CannotRemoveUnrealizedItems, index, count)); if (offsetR < block.ContainerCount) break; offsetR -= block.ContainerCount; } RealizedItemBlock blockR = block as RealizedItemBlock; // de-initialize the containers that are being removed RealizedItemBlock rblock = blockL; int offset = offsetL; while (rblock != blockR || offset <= offsetR) { DependencyObject container = rblock.ContainerAt(offset); UnlinkContainerFromItem(container, rblock.ItemAt(offset)); // DataGrid generates non-GroupItem for NewItemPlaceHolder // Dont recycle in this case. bool isNewItemPlaceHolderWhenGrouping = _generatesGroupItems && !(container is GroupItem); if (isRecycling && !isNewItemPlaceHolderWhenGrouping) { Debug.Assert(!_recyclableContainers.Contains(container), "trying to add a container to the collection twice"); if (_containerType == null) { _containerType = container.GetType(); } else if (_containerType != container.GetType()) { throw new InvalidOperationException(SR.Get(SRID.CannotRecyleHeterogeneousTypes)); } _recyclableContainers.Enqueue(container); } if (++offset >= rblock.ContainerCount && rblock != blockR) { rblock = rblock.Next as RealizedItemBlock; offset = 0; } } // see whether the range hits the edge of a block on either side, // and whether the a`butting block is an unrealized gap bool edgeL = (offsetL == 0); bool edgeR = (offsetR == blockR.ItemCount-1); bool abutL = edgeL && (blockL.Prev is UnrealizedItemBlock); bool abutR = edgeR && (blockR.Next is UnrealizedItemBlock); // determine the target (unrealized) block, // the offset within the target at which to insert items, // and the intial change in cumulative item count UnrealizedItemBlock blockT; ItemBlock predecessor = null; int offsetT; int deltaCount; if (abutL) { blockT = (UnrealizedItemBlock)blockL.Prev; offsetT = blockT.ItemCount; deltaCount = -blockT.ItemCount; } else if (abutR) { blockT = (UnrealizedItemBlock)blockR.Next; offsetT = 0; deltaCount = offsetL; } else { blockT = new UnrealizedItemBlock(); offsetT = 0; deltaCount = offsetL; // remember where the new block goes, so we can insert it later predecessor = (edgeL) ? blockL.Prev : blockL; } // move items within the range to the target block for (block = blockL; block != blockR; block = block.Next) { int itemCount = block.ItemCount; MoveItems(block, offsetL, itemCount-offsetL, blockT, offsetT, deltaCount); offsetT += itemCount-offsetL; offsetL = 0; deltaCount -= itemCount; if (block.ItemCount == 0) block.Remove(); } // the last block in the range is a little special... // Move the last unrealized piece. int remaining = block.ItemCount - 1 - offsetR; MoveItems(block, offsetL, offsetR - offsetL + 1, blockT, offsetT, deltaCount); // Move the remaining realized items RealizedItemBlock blockX = blockR; if (!edgeR) { if (blockL == blockR && !edgeL) { blockX = new RealizedItemBlock(); } MoveItems(block, offsetR+1, remaining, blockX, 0, offsetR+1); } // if we created any new blocks, insert them in the list if (predecessor != null) blockT.InsertAfter(predecessor); if (blockX != blockR) blockX.InsertAfter(blockT); RemoveAndCoalesceBlocksIfNeeded(block); }
void OnItemMoved(object item, int oldIndex, int newIndex) { if (_itemMap == null) { // reentrant call (from RemoveAllInternal) shouldn't happen, // but if it does, don't crash Debug.Assert(false, "unexpected reentrant call to OnItemMoved"); return; } DependencyObject container = null; // the corresponding container int containerCount = 0; UnrealizedItemBlock uib; // search for the moved item GeneratorPosition position; ItemBlock block; int offsetFromBlockStart; int correctIndex; GetBlockAndPosition(item, oldIndex, true, out position, out block, out offsetFromBlockStart, out correctIndex); GeneratorPosition oldPosition = position; RealizedItemBlock rib = block as RealizedItemBlock; if (rib != null) { containerCount = 1; container = rib.ContainerAt(offsetFromBlockStart); } // remove the item, and remove the block if it's now empty MoveItems(block, offsetFromBlockStart + 1, block.ItemCount - offsetFromBlockStart - 1, block, offsetFromBlockStart, 0); --block.ItemCount; RemoveAndCoalesceBlocksIfNeeded(block); // // now insert into the new spot. // position = new GeneratorPosition(-1,0); block = _itemMap.Next; offsetFromBlockStart = newIndex; while (block != _itemMap && offsetFromBlockStart >= block.ItemCount) { offsetFromBlockStart -= block.ItemCount; if (block.ContainerCount > 0) { position.Index += block.ContainerCount; position.Offset = 0; } else { position.Offset += block.ItemCount; } block = block.Next; } position.Offset += offsetFromBlockStart + 1; // if it's an unrealized block, add the item by bumping the count uib = block as UnrealizedItemBlock; if (uib != null) { MoveItems(uib, offsetFromBlockStart, 1, uib, offsetFromBlockStart+1, 0); ++ uib.ItemCount; } // if the item can be added to a previous unrealized block, do so else if ((offsetFromBlockStart == 0 || block == _itemMap) && ((uib = block.Prev as UnrealizedItemBlock) != null)) { ++ uib.ItemCount; } // otherwise, create a new unrealized block else { uib = new UnrealizedItemBlock(); uib.ItemCount = 1; // split the current realized block, if necessary if (offsetFromBlockStart > 0 && (rib = block as RealizedItemBlock) != null) { RealizedItemBlock newBlock = new RealizedItemBlock(); MoveItems(rib, offsetFromBlockStart, rib.ItemCount - offsetFromBlockStart, newBlock, 0, offsetFromBlockStart); newBlock.InsertAfter(rib); position.Index += block.ContainerCount; position.Offset = 1; offsetFromBlockStart = 0; block = newBlock; } uib.InsertBefore(block); } DependencyObject parent = VisualTreeHelper.GetParentInternal(container); // tell layout what happened if (ItemsChanged != null) { ItemsChanged(this, new ItemsChangedEventArgs(NotifyCollectionChangedAction.Move, position, oldPosition, 1, containerCount)); } // unhook the container. Do this after layout has (presumably) removed it from // the UI, so that it doesn't inherit DataContext falsely. if (container != null) { if (parent == null || VisualTreeHelper.GetParentInternal(container) != parent) { UnlinkContainerFromItem(container, item); } else { // If the container has the same visual parent as before then that means that // the container was just repositioned within the parent's VisualCollection. // we don't need to unlink the container, but we do need to re-realize the block. Realize(uib, offsetFromBlockStart, item, container); } } // fix up the AlternationIndex on containers affected by the move if (_alternationCount > 0) { // start with the smaller of the two positions, and proceed forward. // This tends to preserve the AlternatonIndex on containers at the // front of the list, as users expect int index = Math.Min(oldIndex, newIndex); GetBlockAndPosition(index, out position, out block, out offsetFromBlockStart); SetAlternationIndex(block, offsetFromBlockStart, GeneratorDirection.Forward); } }
// Called when an item is added to the items collection void OnItemAdded(object item, int index) { if (_itemMap == null) { // reentrant call (from RemoveAllInternal) shouldn't happen, // but if it does, don't crash Debug.Assert(false, "unexpected reentrant call to OnItemAdded"); return; } ValidateAndCorrectIndex(item, ref index); GeneratorPosition position = new GeneratorPosition(-1,0); // find the block containing the new item ItemBlock block = _itemMap.Next; int offset = index; while (block != _itemMap && offset >= block.ItemCount) { offset -= block.ItemCount; position.Index += block.ContainerCount; block = block.Next; } position.Offset = offset + 1; // if it's an unrealized block, add the item by bumping the count UnrealizedItemBlock uib = block as UnrealizedItemBlock; if (uib != null) { MoveItems(uib, offset, 1, uib, offset+1, 0); ++ uib.ItemCount; } // if the item can be added to a previous unrealized block, do so else if ((offset == 0 || block == _itemMap) && ((uib = block.Prev as UnrealizedItemBlock) != null)) { ++ uib.ItemCount; } // otherwise, create a new unrealized block else { uib = new UnrealizedItemBlock(); uib.ItemCount = 1; // split the current realized block, if necessary RealizedItemBlock rib; if (offset > 0 && (rib = block as RealizedItemBlock) != null) { RealizedItemBlock newBlock = new RealizedItemBlock(); MoveItems(rib, offset, rib.ItemCount - offset, newBlock, 0, offset); newBlock.InsertAfter(rib); position.Index += block.ContainerCount; position.Offset = 1; block = newBlock; } uib.InsertBefore(block); } // tell generators what happened if (MapChanged != null) { MapChanged(null, index, +1, uib, 0, 0); } // tell layout what happened if (ItemsChanged != null) { ItemsChanged(this, new ItemsChangedEventArgs(NotifyCollectionChangedAction.Add, position, 1, 0)); } }
// "Realize" the item in a block at the given offset, to be // the given item with corresponding container. This means updating // the item map data structure so that the item belongs to a Realized block. // It also requires updating the state of every generator to track the // changes we make here. void Realize(UnrealizedItemBlock block, int offset, object item, DependencyObject container) { RealizedItemBlock prevR, nextR; RealizedItemBlock newBlock; // new location of the target item int newOffset; // its offset within the new block int deltaCount; // diff between cumulative item count of block and newBlock // if we're realizing the leftmost item and there's room in the // previous block, move it there if (offset == 0 && (prevR = block.Prev as RealizedItemBlock) != null && prevR.ItemCount < ItemBlock.BlockSize) { newBlock = prevR; newOffset = prevR.ItemCount; MoveItems(block, offset, 1, newBlock, newOffset, -prevR.ItemCount); MoveItems(block, 1, block.ItemCount, block, 0, +1); } // if we're realizing the rightmost item and there's room in the // next block, move it there else if (offset == block.ItemCount - 1 && (nextR = block.Next as RealizedItemBlock) != null && nextR.ItemCount < ItemBlock.BlockSize) { newBlock = nextR; newOffset = 0; MoveItems(newBlock, 0, newBlock.ItemCount, newBlock, 1, -1); MoveItems(block, offset, 1, newBlock, newOffset, offset); } // otherwise we need a new block for the target item else { newBlock = new RealizedItemBlock(); newOffset = 0; deltaCount = offset; // if target is leftmost item, insert it before remaining items if (offset == 0) { newBlock.InsertBefore(block); MoveItems(block, offset, 1, newBlock, newOffset, 0); MoveItems(block, 1, block.ItemCount, block, 0, +1); } // if target is rightmost item, insert it after remaining items else if (offset == block.ItemCount - 1) { newBlock.InsertAfter(block); MoveItems(block, offset, 1, newBlock, newOffset, offset); } // otherwise split the block into two, with the target in the middle else { UnrealizedItemBlock newUBlock = new UnrealizedItemBlock(); newUBlock.InsertAfter(block); newBlock.InsertAfter(block); MoveItems(block, offset+1, block.ItemCount-offset-1, newUBlock, 0, offset+1); MoveItems(block, offset, 1, newBlock, 0, offset); } } RemoveAndCoalesceBlocksIfNeeded(block); // add the new target to the map newBlock.RealizeItem(newOffset, item, container); }
// Called when an item is added to the items collection void OnItemAdded(object item, int index) { ValidateAndCorrectIndex(item, ref index); GeneratorPosition position = new GeneratorPosition(-1,0); // find the block containing the new item ItemBlock block = _itemMap.Next; int offset = index; while (block != _itemMap && offset >= block.ItemCount) { offset -= block.ItemCount; position.Index += block.ContainerCount; block = block.Next; } position.Offset = offset + 1; // if it's an unrealized block, add the item by bumping the count UnrealizedItemBlock uib = block as UnrealizedItemBlock; if (uib != null) { MoveItems(uib, offset, 1, uib, offset+1, 0); ++ uib.ItemCount; } // if the item can be added to a previous unrealized block, do so else if ((offset == 0 || block == _itemMap) && ((uib = block.Prev as UnrealizedItemBlock) != null)) { ++ uib.ItemCount; } // otherwise, create a new unrealized block else { uib = new UnrealizedItemBlock(); uib.ItemCount = 1; // split the current realized block, if necessary RealizedItemBlock rib; if (offset > 0 && (rib = block as RealizedItemBlock) != null) { RealizedItemBlock newBlock = new RealizedItemBlock(); MoveItems(rib, offset, rib.ItemCount - offset, newBlock, 0, offset); newBlock.InsertAfter(rib); position.Index += block.ContainerCount; position.Offset = 1; block = newBlock; } uib.InsertBefore(block); } if (_traceLog != null) _traceLog.Add("OnItemAdded {0} index = {1}", TraceLog.IdFor(item), index); // tell generators what happened if (MapChanged != null) { MapChanged(null, index, +1, null, 0, 0); } // tell layout what happened if (ItemsChanged != null) { ItemsChanged(this, new ItemsChangedEventArgs(NotifyCollectionChangedAction.Add, position, 1, 0)); } }