예제 #1
0
        public void UpdatePin(UIElement element, bool addPin)
        {
            var parent = CachedVisualTreeHelpers.GetParent(element);
            var child  = (DependencyObject)element;

            while (parent != null)
            {
                if (parent is ItemsRepeater repeater)
                {
                    var virtInfo = ItemsRepeater.GetVirtualizationInfo(child as UIElement);
                    if (virtInfo.IsRealized)
                    {
                        if (addPin)
                        {
                            virtInfo.AddPin();
                        }
                        else if (virtInfo.IsPinned)
                        {
                            if (virtInfo.RemovePin() == 0)
                            {
                                // ElementFactory is invoked during the measure pass.
                                // We will clear the element then.
                                repeater.InvalidateMeasure();
                            }
                        }
                    }
                }

                child  = parent;
                parent = CachedVisualTreeHelpers.GetParent(child);
            }
        }
예제 #2
0
        public void ClearElementToElementFactory(UIElement element)
        {
            m_owner.OnElementClearing(element);

            if (m_owner.ItemTemplateShim != null)
            {
                if (m_ElementFactoryRecycleArgs == null)
                {
                    // Create one.
                    m_ElementFactoryRecycleArgs = new ElementFactoryRecycleArgs();
                }

                var context = m_ElementFactoryRecycleArgs;
                context.Element = element;
                context.Parent  = m_owner;

                m_owner.ItemTemplateShim.RecycleElement(context);

                context.Element = null;
                context.Parent  = null;
            }
            else
            {
                // No ItemTemplate to recycle to, remove the element from the children collection.
                var children   = m_owner.Children;
                int childIndex = 0;
                childIndex = children.IndexOf(element);
                if (childIndex < 0)
                {
                    throw new InvalidOperationException("ItemsRepeater's child not found in its Children collection.");
                }

                children.RemoveAt(childIndex);
            }

            var virtInfo = ItemsRepeater.GetVirtualizationInfo(element);

            virtInfo.MoveOwnershipToElementFactory();
            m_phaser.StopPhasing(element, virtInfo);
            if (m_lastFocusedElement == element)
            {
                // Focused element is going away. Remove the tracked last focused element
                // and pick a reasonable next focus if we can find one within the layout
                // realized elements.
                int clearedIndex = virtInfo.Index;
                MoveFocusFromClearedIndex(clearedIndex);
            }

            REPEATER_TRACE_PERF("ElementCleared");
        }
예제 #3
0
        public void Add(UIElement element)
        {
            global::System.Diagnostics.Debug.Assert(m_owner.ItemsSourceView.HasKeyIndexMapping);

            var virtInfo = ItemsRepeater.GetVirtualizationInfo(element);
            var key      = virtInfo.UniqueId;

            if (m_elementMap.ContainsKey(key))
            {
                throw new InvalidOperationException($"The unique id provided ({virtInfo.UniqueId}) is not unique.");
            }

            m_elementMap.Add(key, element);
        }
예제 #4
0
        void UpdateFocusedElement()
        {
            UIElement focusedElement = null;

            if (FocusManager.GetFocusedElement() is DependencyObject child)
            {
                var parent = CachedVisualTreeHelpers.GetParent(child);
                var owner  = (UIElement)m_owner;

                // Find out if the focused element belongs to one of our direct
                // children.
                while (parent != null)
                {
                    if (parent is ItemsRepeater repeater)
                    {
                        var element = child as UIElement;
                        if (repeater == owner && ItemsRepeater.GetVirtualizationInfo(element).IsRealized)
                        {
                            focusedElement = element;
                        }

                        break;
                    }

                    child  = parent;
                    parent = CachedVisualTreeHelpers.GetParent(child);
                }
            }

            // If the focused element has changed,
            // we need to unpin the old one and pin the new one.
            if (m_lastFocusedElement != focusedElement)
            {
                if (m_lastFocusedElement != null)
                {
                    UpdatePin(m_lastFocusedElement, false /* addPin */);
                }

                if (focusedElement != null)
                {
                    UpdatePin(focusedElement, true /* addPin */);
                }

                m_lastFocusedElement = focusedElement;
            }
        }
            public ChildrenInTabFocusOrderIterator(ItemsRepeater repeater)
            {
                var children = repeater.Children;

                m_realizedChildren = new List <KeyValuePair <int, UIElement> >(children.Count);

                // Filter out unrealized children.
                for (var i = 0; i < children.Count; ++i)
                {
                    var element  = children[i];
                    var virtInfo = ItemsRepeater.GetVirtualizationInfo(element);
                    if (virtInfo.IsRealized)
                    {
                        m_realizedChildren.Add(new KeyValuePair <int, UIElement>(virtInfo.Index, element));
                    }
                }

                // Sort children by index.
                m_realizedChildren.Sort((lhs, rhs) => lhs.Key - rhs.Key);
            }
예제 #6
0
        public void ClearElement(UIElement element, bool isClearedDueToCollectionChange)
        {
            var  virtInfo = ItemsRepeater.GetVirtualizationInfo(element);
            int  index    = virtInfo.Index;
            bool cleared  =
                ClearElementToUniqueIdResetPool(element, virtInfo) ||
                ClearElementToAnimator(element, virtInfo) ||
                ClearElementToPinnedPool(element, virtInfo, isClearedDueToCollectionChange);

            if (!cleared)
            {
                ClearElementToElementFactory(element);
            }

            // Both First and Last indices need to be valid or default.
            MUX_ASSERT((m_firstRealizedElementIndexHeldByLayout == FirstRealizedElementIndexDefault && m_lastRealizedElementIndexHeldByLayout == LastRealizedElementIndexDefault) ||
                       (m_firstRealizedElementIndexHeldByLayout != FirstRealizedElementIndexDefault && m_lastRealizedElementIndexHeldByLayout != LastRealizedElementIndexDefault));

            if (index == m_firstRealizedElementIndexHeldByLayout && index == m_lastRealizedElementIndexHeldByLayout)
            {
                // First and last were pointing to the same element and that is going away.
                InvalidateRealizedIndicesHeldByLayout();
            }
            else if (index == m_firstRealizedElementIndexHeldByLayout)
            {
                // The FirstElement is going away, shrink the range by one.
                ++m_firstRealizedElementIndexHeldByLayout;
            }
            else if (index == m_lastRealizedElementIndexHeldByLayout)
            {
                // Last element is going away, shrink the range by one at the end.
                --m_lastRealizedElementIndexHeldByLayout;
            }
            else
            {
                // Index is either outside the range we are keeping track of or inside the range.
                // In both these cases, we just keep the range we have. If this clear was due to
                // a collection change, then in the CollectionChanged event, we will invalidate these guys.
            }
        }
예제 #7
0
        UIElement GetElementFromUniqueIdResetPool(int index)
        {
            UIElement element = null;

            // See if you can get it from the reset pool.
            if (m_isDataSourceStableResetPending)
            {
                element = m_resetPool.Remove(index);
                if (element != null)
                {
                    // Make sure that the index is updated to the current one
                    var virtInfo = ItemsRepeater.GetVirtualizationInfo(element);
                    virtInfo.MoveOwnershipToLayoutFromUniqueIdResetPool();
                    UpdateElementIndex(element, virtInfo, index);

                    // Update realized indices
                    m_firstRealizedElementIndexHeldByLayout = Math.Min(m_firstRealizedElementIndexHeldByLayout, index);
                    m_lastRealizedElementIndexHeldByLayout  = Math.Max(m_lastRealizedElementIndexHeldByLayout, index);
                }
            }

            return(element);
        }
예제 #8
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;
            }
        }
예제 #9
0
 public PinnedElementInfo(UIElement element)
 {
     m_pinnedElement = element;
     m_virtInfo      = ItemsRepeater.GetVirtualizationInfo(element);
 }