internal override AddRemoveLayoutResult RemoveItem(object changedItem, object addRemoveItem, int addRemoveItemIndex) { int removedItemsCount = 1; int removedLinesCount = 0; bool isVisible = true; GroupInfo changedGroupInfo = this.GetGroupInfo(changedItem); bool changedGroupIsRoot = changedGroupInfo == null; int flatSlot = -1; int rowSlot = -1; int level = changedGroupIsRoot ? 0 : changedGroupInfo.Level + 1; IGroup removedGroup = addRemoveItem as IGroup; // We added/removed group so we need to index all subgroups. if (removedGroup != null && changedItem != addRemoveItem) { GroupInfo groupInfo = this.GetGroupInfo(addRemoveItem); System.Diagnostics.Debug.Assert(groupInfo != null, "Cannot remove group that are not indexed."); flatSlot = groupInfo.Index; isVisible = groupInfo.IsVisible(); removedItemsCount = groupInfo.GetLineSpan(); removedLinesCount = removedItemsCount; if (!removedGroup.HasItems) { foreach (var nextGroupIndex in this.GroupHeadersTable.GetIndexes(flatSlot + 1)) { GroupInfo nextGroupInfo = this.GroupHeadersTable.GetValueAt(nextGroupIndex); if (nextGroupInfo.Level <= groupInfo.Level) { break; } this.RemoveGroupInfo(nextGroupInfo); } } this.UpdateGroupHeadersTable(groupInfo.Index, -removedItemsCount); this.itemInfoTable.Remove(removedGroup); this.GroupHeadersTable.RemoveIndexesAndValues(flatSlot, removedItemsCount); if (isVisible) { if (groupInfo.IsExpanded) { int collapsedItems = removedItemsCount - this.GetCollapsedSlotsCount(flatSlot, flatSlot + removedItemsCount - 1); this.VisibleLineCount -= collapsedItems; } else { this.VisibleLineCount -= 1; } } this.collapsedSlotsTable.RemoveIndexesAndValues(flatSlot, removedItemsCount); if (changedGroupInfo != null) { // Update the group last slot. changedGroupInfo.LastSubItemSlot -= removedItemsCount; } } else { // We added new item (not group) into root group. if (changedGroupIsRoot) { if (isVisible) { removedLinesCount = this.GetRemovedSlots(this.ItemsSource.Count, removedItemsCount); this.VisibleLineCount -= removedLinesCount; flatSlot = addRemoveItemIndex; } // slot should be already correct. // No need to correct collapsed slots. They should be empty (e.g. no groups to collapse). System.Diagnostics.Debug.Assert(this.GroupHeadersTable.IsEmpty, "GroupHeaders table should be empty."); System.Diagnostics.Debug.Assert(this.collapsedSlotsTable.IsEmpty, "CollapsedSlots table should be empty since we don't have groups."); } else { var children = (changedGroupInfo.Item as IGroup).Items.Count; // Be carefull that this represents lines/slots rather than items. Also consider if collapsed table should use this removedLinesCount = this.GetRemovedSlots(children, removedItemsCount); flatSlot = changedGroupInfo.Index + 1 + addRemoveItemIndex; // Update the group last slot. changedGroupInfo.LastSubItemSlot -= removedItemsCount; this.GroupHeadersTable.RemoveIndex(flatSlot); isVisible = changedGroupInfo.IsExpanded && changedGroupInfo.IsVisible(); if (isVisible) { this.collapsedSlotsTable.RemoveIndexes(flatSlot, removedLinesCount); this.VisibleLineCount -= removedLinesCount; } else { this.collapsedSlotsTable.RemoveIndexesAndValues(flatSlot, removedLinesCount); } this.UpdateGroupHeadersTable(changedGroupInfo.Index, -removedItemsCount); } } rowSlot = this.GetRowSlotFromFlatSlot(flatSlot); // Update parent groups last slot. CompactLayout.UpdateParentGroupInfosLastSlot(-removedItemsCount, changedGroupInfo); // Update TotalCount. this.TotalSlotCount -= removedLinesCount; this.RemoveFromRenderInfo(removedLinesCount, rowSlot); var layoutResult = new AddRemoveLayoutResult(rowSlot, removedLinesCount); foreach (var strategy in this.LayoutStrategies) { strategy.OnItemRemoved(layoutResult); } // return result indexes so that changed can be reflected to UI. return(layoutResult); }
internal override AddRemoveLayoutResult AddItem(object changedItem, object addRemoveItem, int addRemoveItemIndex) { int addedItemsCount = 1; bool isVisible = true; var groupInfo = this.GetGroupInfo(changedItem); bool changedGroupIsRoot = groupInfo == null; int flatSlot = -1; // = addRemoveItemIndex; int rowSlot = -1; int addedLinesCount = 0; int level = changedGroupIsRoot ? 0 : groupInfo.Level + 1; // We added/removed group so we need to index all subgroups. if (addRemoveItem is IGroup && changedItem != addRemoveItem) { flatSlot = this.GetInsertedGroupSlot(changedItem, addRemoveItemIndex); isVisible = groupInfo != null ? (groupInfo.IsExpanded && groupInfo.IsVisible()) : true; // We give a list in which to insert groups so that we can manually correct the indexes in groupHeadersTable. List <GroupInfo> insertedGroups = new List <GroupInfo>(); addedItemsCount = this.CountAndPopulateTables(addRemoveItem, flatSlot, level, this.GroupLevels, groupInfo, false, insertedGroups); addedLinesCount = addedItemsCount; bool first = true; int innerMostSlot = flatSlot; foreach (var newGroupInfo in insertedGroups) { int groupInfoToInsertSpan = newGroupInfo.GetLineSpan(); if (first) { first = false; this.GroupHeadersTable.InsertIndexes(newGroupInfo.Index, groupInfoToInsertSpan); if (isVisible) { this.collapsedSlotsTable.InsertIndexes(flatSlot, addedItemsCount); this.VisibleLineCount += addedItemsCount; } else { this.collapsedSlotsTable.InsertIndexesAndValues(flatSlot, addedItemsCount, false); } } this.GroupHeadersTable.AddValue(newGroupInfo.Index, newGroupInfo); // We get the inner most group so that we update groups after inner most (the upper one are newly created and their index and last slot count are correct. // We get it only if the change is in the root group. In existing groups we know the correct slot. if (changedGroupIsRoot) { innerMostSlot = newGroupInfo.Index; } } // Update groups after current one. this.UpdateGroupHeadersTable(innerMostSlot, addedItemsCount); if (groupInfo != null) { // Update the group last slot. groupInfo.LastSubItemSlot += addedItemsCount; } } else { // We added new item (not group) into root group. if (changedGroupIsRoot) { if (isVisible) { addedLinesCount = this.GetAddedSlots(this.ItemsSource.Count, addedItemsCount); this.VisibleLineCount += addedLinesCount; flatSlot = addRemoveItemIndex; } // slot should be already correct. // No need to correct collapsed slots. They should be empty (e.g. no groups to collapse). System.Diagnostics.Debug.Assert(this.GroupHeadersTable.IsEmpty, "GroupHeaders table should be empty."); System.Diagnostics.Debug.Assert(this.collapsedSlotsTable.IsEmpty, "CollapsedSlots table should be empty since we don't have groups."); } else { // We added new item (not group) into existing bottom level group (not root group). var children = (groupInfo.Item as IGroup).Items.Count; addedLinesCount = this.GetAddedSlots(children, addedItemsCount); flatSlot = groupInfo.Index + 1 + addRemoveItemIndex; // We give a list in which to insert groups so that we can manually correct the indexes in groupHeadersTable. // Update the group last slot. groupInfo.LastSubItemSlot += addedItemsCount; if (addedItemsCount > 0) { this.GroupHeadersTable.InsertIndex(flatSlot); } isVisible = groupInfo.IsExpanded && groupInfo.IsVisible(); if (addedItemsCount > 0) { if (isVisible) { this.collapsedSlotsTable.InsertIndexes(flatSlot, addedItemsCount); this.VisibleLineCount += addedLinesCount; } else { this.collapsedSlotsTable.InsertIndexesAndValues(flatSlot, addedItemsCount, false); } } this.UpdateGroupHeadersTable(groupInfo.Index, addedItemsCount); } } rowSlot = this.GetRowSlotFromFlatSlot(flatSlot); // Update parent groups last slot. CompactLayout.UpdateParentGroupInfosLastSlot(addedItemsCount, groupInfo); // Update TotalCount. this.TotalSlotCount += addedLinesCount; // Update Visible line count if not collapsed. double length = isVisible ? this.averageItemLength : 0; if (addedLinesCount >= 0) { this.InsertToRenderInfo(rowSlot, null, addedLinesCount); } var layoutResult = new AddRemoveLayoutResult(rowSlot, addedLinesCount); foreach (var strategy in this.LayoutStrategies) { strategy.OnItemAdded(layoutResult); } // return result indexes so that changed can be reflected to UI. return(layoutResult); }