Exemplo n.º 1
0
        public void OnItemsSourceChanged(object _, NotifyCollectionChangedEventArgs args)
        {
            // Note: For items that have been removed, the index will not be touched. It will hold
            // the old index before it was removed. It is not valid anymore.
            switch (args.Action)
            {
            case NotifyCollectionChangedAction.Add:
            {
                var newIndex = args.NewStartingIndex;
                var newCount = args.NewItems.Count;
                EnsureFirstLastRealizedIndices();
                if (newIndex <= m_lastRealizedElementIndexHeldByLayout)
                {
                    m_lastRealizedElementIndexHeldByLayout += newCount;
                    var children   = m_owner.Children;
                    var childCount = children.Count;
                    for (int i = 0; i < childCount; ++i)
                    {
                        var element   = children[i];
                        var virtInfo  = ItemsRepeater.GetVirtualizationInfo(element);
                        var dataIndex = virtInfo.Index;

                        if (virtInfo.IsRealized && dataIndex >= newIndex)
                        {
                            UpdateElementIndex(element, virtInfo, dataIndex + newCount);
                        }
                    }
                }
                else
                {
                    // Indices held by layout are not affected
                    // We could still have items in the pinned elements that need updates. This is usually a very small vector.
                    for (int i = 0; i < m_pinnedPool.Count; ++i)
                    {
                        var elementInfo = m_pinnedPool[i];
                        var virtInfo    = elementInfo.VirtualizationInfo;
                        var dataIndex   = virtInfo.Index;

                        if (virtInfo.IsRealized && dataIndex >= newIndex)
                        {
                            var element = elementInfo.PinnedElement;
                            UpdateElementIndex(element, virtInfo, dataIndex + newCount);
                        }
                    }
                }

                break;
            }

            case NotifyCollectionChangedAction.Replace:
            {
                // Requirement: oldStartIndex == newStartIndex. It is not a replace if this is not true.
                // Two cases here
                // case 1: oldCount == newCount
                //         indices are not affected. nothing to do here.
                // case 2: oldCount != newCount
                //         Replaced with less or more items. This is like an insert or remove
                //         depending on the counts.
                var oldStartIndex    = args.OldStartingIndex;
                var newStartingIndex = args.NewStartingIndex;
                var oldCount         = (int)(args.OldItems.Count);
                var newCount         = (int)(args.NewItems.Count);
                if (oldStartIndex != newStartingIndex)
                {
                    throw new InvalidOperationException("Replace is only allowed with OldStartingIndex equals to NewStartingIndex.");
                }

                if (oldCount == 0)
                {
                    throw new InvalidOperationException("Replace notification with args.OldItemsCount value of 0 is not allowed. Use Insert action instead.");
                }

                if (newCount == 0)
                {
                    throw new InvalidOperationException("Replace notification with args.NewItemCount value of 0 is not allowed. Use Remove action instead.");
                }

                int countChange = newCount - oldCount;
                if (countChange != 0)
                {
                    // countChange > 0 : countChange items were added
                    // countChange < 0 : -countChange  items were removed
                    var children = m_owner.Children;
                    for (int i = 0; i < children.Count; ++i)
                    {
                        var element   = children[i];
                        var virtInfo  = ItemsRepeater.GetVirtualizationInfo(element);
                        var dataIndex = virtInfo.Index;

                        if (virtInfo.IsRealized)
                        {
                            if (dataIndex >= oldStartIndex + oldCount)
                            {
                                UpdateElementIndex(element, virtInfo, dataIndex + countChange);
                            }
                        }
                    }

                    EnsureFirstLastRealizedIndices();
                    m_lastRealizedElementIndexHeldByLayout += countChange;
                }

                break;
            }

            case NotifyCollectionChangedAction.Remove:
            {
                var oldStartIndex = args.OldStartingIndex;
                var oldCount      = (int)(args.OldItems.Count);
                var children      = m_owner.Children;
                for (int i = 0; i < children.Count; ++i)
                {
                    var element   = children[i];
                    var virtInfo  = ItemsRepeater.GetVirtualizationInfo(element);
                    var dataIndex = virtInfo.Index;

                    if (virtInfo.IsRealized)
                    {
                        if (virtInfo.AutoRecycleCandidate && oldStartIndex <= dataIndex && dataIndex < oldStartIndex + oldCount)
                        {
                            // If we are doing the mapping, remove the element who's data was removed.
                            m_owner.ClearElementImpl(element);
                        }
                        else if (dataIndex >= (oldStartIndex + oldCount))
                        {
                            UpdateElementIndex(element, virtInfo, dataIndex - oldCount);
                        }
                    }
                }

                InvalidateRealizedIndicesHeldByLayout();
                break;
            }

            case NotifyCollectionChangedAction.Reset:
                // If we get multiple resets back to back before
                // running layout, we dont have to clear all the elements again.
                if (!m_isDataSourceStableResetPending)
                {
#if DEBUG
                    // There should be no elements in the reset pool at this time.
                    MUX_ASSERT(m_resetPool.IsEmpty);
#endif

                    if (m_owner.ItemsSourceView.HasKeyIndexMapping)
                    {
                        m_isDataSourceStableResetPending = true;
                    }

                    // Walk through all the elements and make sure they are cleared, they will go into
                    // the stable id reset pool.
                    var children = m_owner.Children;
                    for (int i = 0; i < children.Count; ++i)
                    {
                        var element  = children[i];
                        var virtInfo = ItemsRepeater.GetVirtualizationInfo(element);
                        if (virtInfo.IsRealized && virtInfo.AutoRecycleCandidate)
                        {
                            m_owner.ClearElementImpl(element);
                        }
                    }
                }

                InvalidateRealizedIndicesHeldByLayout();

                break;
            }
        }