Esempio n. 1
0
        public UIElement GetElement(int index, bool forceCreate, bool suppressAutoRecycle)
        {
            UIElement element = forceCreate ? null : GetElementIfAlreadyHeldByLayout(index);

            if (element == null)
            {
                // check if this is the anchor made through repeater in preparation
                // for a bring into view.
                if (m_owner.MadeAnchor is UIElement madeAnchor)
                {
                    var anchorVirtInfo = ItemsRepeater.TryGetVirtualizationInfo(madeAnchor);
                    if (anchorVirtInfo.Index == index)
                    {
                        element = madeAnchor;
                    }
                }
            }
            if (element == null)
            {
                element = GetElementFromUniqueIdResetPool(index);
            }
            ;
            if (element == null)
            {
                element = GetElementFromPinnedElements(index);
            }
            if (element == null)
            {
                element = GetElementFromElementFactory(index);
            }

            var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(element);

            if (suppressAutoRecycle)
            {
                virtInfo.AutoRecycleCandidate = false;
            }
            else
            {
                virtInfo.AutoRecycleCandidate = true;
                virtInfo.KeepAlive            = true;
            }

            return(element);
        }
Esempio n. 2
0
        // We optimize for the case where index is not realized to return null as quickly as we can.
        // Flow layouts manage containers on their own and will never ask for an index that is already realized.
        // If an index that is realized is requested by the layout, we unfortunately have to walk the
        // children. Not ideal, but a reasonable default to provide consistent behavior between virtualizing
        // and non-virtualizing hosts.
        private UIElement GetElementIfAlreadyHeldByLayout(int index)
        {
            UIElement element = null;

            bool cachedFirstLastIndicesInvalid = m_firstRealizedElementIndexHeldByLayout == FirstRealizedElementIndexDefault;

            Debug.Assert(!cachedFirstLastIndicesInvalid || m_lastRealizedElementIndexHeldByLayout == LastRealizedElementIndexDefault);

            bool isRequestedIndexInRealizedRange = (m_firstRealizedElementIndexHeldByLayout <= index && index <= m_lastRealizedElementIndexHeldByLayout);

            if (cachedFirstLastIndicesInvalid || isRequestedIndexInRealizedRange)
            {
                // Both First and Last indices need to be valid or default.
                Debug.Assert((m_firstRealizedElementIndexHeldByLayout == FirstRealizedElementIndexDefault && m_lastRealizedElementIndexHeldByLayout == LastRealizedElementIndexDefault) ||
                             (m_firstRealizedElementIndexHeldByLayout != FirstRealizedElementIndexDefault && m_lastRealizedElementIndexHeldByLayout != LastRealizedElementIndexDefault));

                var children = m_owner.Children;
                for (int i = 0; i < children.Count; ++i)
                {
                    var child    = children[i];
                    var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(child);
                    if (virtInfo != null && virtInfo.IsHeldByLayout)
                    {
                        // Only give back elements held by layout. If someone else is holding it, they will be served by other methods.
                        int childIndex = virtInfo.Index;
                        m_firstRealizedElementIndexHeldByLayout = Math.Min(m_firstRealizedElementIndexHeldByLayout, childIndex);
                        m_lastRealizedElementIndexHeldByLayout  = Math.Max(m_lastRealizedElementIndexHeldByLayout, childIndex);
                        if (virtInfo.Index == index)
                        {
                            element = child;
                            // If we have valid first/last indices, we don't have to walk the rest, but if we
                            // do not, then we keep walking through the entire children collection to get accurate
                            // indices once.
                            if (!cachedFirstLastIndicesInvalid)
                            {
                                break;
                            }
                        }
                    }
                }
            }

            return(element);
        }
Esempio n. 3
0
        private UIElement FindFocusCandidate(int clearedIndex, ref UIElement focusedChild)
        {
            // Walk through all the children and find elements with index before and after the cleared index.
            // Note that during a delete the next element would now have the same index.
            int       previousIndex   = int.MinValue;
            int       nextIndex       = int.MaxValue;
            UIElement nextElement     = null;
            UIElement previousElement = null;
            var       children        = m_owner.Children;

            for (int i = 0; i < children.Count; ++i)
            {
                var child    = children[i];
                var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(child);
                if (virtInfo != null && virtInfo.IsHeldByLayout)
                {
                    int currentIndex = virtInfo.Index;
                    if (currentIndex < clearedIndex)
                    {
                        if (currentIndex > previousIndex)
                        {
                            previousIndex   = currentIndex;
                            previousElement = child;
                        }
                    }
                    else if (currentIndex >= clearedIndex)
                    {
                        // Note that we use >= above because if we deleted the focused element,
                        // the next element would have the same index now.
                        if (currentIndex < nextIndex)
                        {
                            nextIndex   = currentIndex;
                            nextElement = child;
                        }
                    }
                }
            }

            // Find the next element if one exists, if not use the previous element.
            // If the container itself is not focusable, find a descendent that is.
            UIElement focusCandidate = null;

            if (nextElement != null)
            {
                focusedChild = nextElement as UIElement;
                if (nextElement.Focus())
                {
                    focusCandidate = nextElement;
                }
                else if (nextElement.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)))
                {
                    focusCandidate = FocusManager.GetFocusedElement(nextElement) as UIElement;
                }
            }

            if (focusCandidate == null && previousElement != null)
            {
                focusedChild = previousElement as UIElement;
                if (previousElement.Focus())
                {
                    focusCandidate = previousElement;
                }
                else if (previousElement.MoveFocus(new TraversalRequest(FocusNavigationDirection.Last)))
                {
                    focusCandidate = FocusManager.GetFocusedElement(previousElement) as UIElement;
                }
            }

            return(focusCandidate);
        }
Esempio n. 4
0
        private UIElement GetElementFromElementFactory(int index)
        {
            // The view generator is the provider of last resort.
            var data = m_owner.ItemsSourceView.GetAt(index);

            UIElement initElement()
            {
                var providedElementFactory = m_owner.ItemTemplateShim;

                if (providedElementFactory == null)
                {
                    if (data is UIElement dataAsElement)
                    {
                        return(dataAsElement);
                    }
                }

                IElementFactoryShim initElementFactory()
                {
                    if (providedElementFactory == null)
                    {
                        var factory = XamlReader.Parse("<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'><TextBlock Text='{Binding}'/></DataTemplate>") as DataTemplate;
                        m_owner.ItemTemplate = factory;
                        return(m_owner.ItemTemplateShim);
                    }
                    return(providedElementFactory);
                }

                var elementFactory = initElementFactory();

                ElementFactoryGetArgs initArgs()
                {
                    if (m_ElementFactoryGetArgs == null)
                    {
                        m_ElementFactoryGetArgs = new ElementFactoryGetArgs();
                    }
                    return(m_ElementFactoryGetArgs);
                }

                var args = initArgs();

                try
                {
                    args.Data   = data;
                    args.Parent = m_owner;
                    args.Index  = index;

                    return(elementFactory.GetElement(args));
                }
                finally
                {
                    args.Data   = null;
                    args.Parent = null;
                }
            }

            var element = initElement();

            var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(element);

            if (virtInfo == null)
            {
                virtInfo = ItemsRepeater.CreateAndInitializeVirtualizationInfo(element);
            }
            else
            {
                // View obtained from ElementFactory already has a VirtualizationInfo attached to it
                // which means that the element has been recycled and not created from scratch.
            }

            if (data != element)
            {
                if (element is FrameworkElement elementAsFE)
                {
                    // Set data context only if no x:Bind was used. ie. No data template component on the root.
                    // If the passed in data is a UIElement and is different from the element returned by
                    // the template factory then we need to propagate the DataContext.
                    // Otherwise just set the DataContext on the element as the data.
                    object initElementDataContext()
                    {
                        if (data is FrameworkElement dataAsElement)
                        {
                            var dataDataContext = dataAsElement.DataContext;
                            if (dataDataContext != null)
                            {
                                return(dataDataContext);
                            }
                        }
                        return(data);
                    }

                    var elementDataContext = initElementDataContext();

                    elementAsFE.DataContext = elementDataContext;
                }
                else
                {
                    Debug.Assert(false, "Element returned by factory is not a FrameworkElement!");
                }
            }

            virtInfo.MoveOwnershipToLayoutFromElementFactory(
                index,
                /* uniqueId: */
                m_owner.ItemsSourceView.HasKeyIndexMapping ?
                m_owner.ItemsSourceView.KeyFromIndex(index) :
                string.Empty);

            // The view generator is the only provider that prepares the element.
            var repeater = m_owner;

            // Add the element to the children collection here before raising OnElementPrepared so
            // that handlers can walk up the tree in case they want to find their IndexPath in the
            // nested case.
            var children = repeater.Children;

            if (CachedVisualTreeHelpers.GetParent(element) != repeater)
            {
                children.Add(element);
            }

            repeater.AnimationManager.OnElementPrepared(element);

            repeater.OnElementPrepared(element, index);

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

            return(element);
        }
Esempio n. 5
0
        private UIElement GetElementFromElementFactory(int index)
        {
            // The view generator is the provider of last resort.
            var data = m_owner.ItemsSourceView.GetAt(index);

            var itemTemplateFactory = m_owner.ItemTemplateShim;

            UIElement element = null;
            bool      itemsSourceContainsElements = false;

            if (itemTemplateFactory == null)
            {
                element = data as UIElement;
                // No item template provided and ItemsSource contains objects derived from UIElement.
                // In this case, just use the data directly as elements.
                itemsSourceContainsElements = element != null;
            }

            if (element == null)
            {
                if (itemTemplateFactory == null)
                {
                    // If no ItemTemplate was provided, use a default
                    var factory = XamlReader.Parse("<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'><TextBlock Text='{Binding}'/></DataTemplate>") as DataTemplate;
                    m_owner.ItemTemplate = factory;
                    itemTemplateFactory  = m_owner.ItemTemplateShim;
                }

                if (m_ElementFactoryGetArgs == null)
                {
                    // Create one.
                    m_ElementFactoryGetArgs = new ElementFactoryGetArgs();
                }

                var args = m_ElementFactoryGetArgs;
                args.Data   = data;
                args.Parent = m_owner;
                args.Index  = index;

                element = itemTemplateFactory.GetElement(args);

                args.Data   = null;
                args.Parent = null;
            }

            var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(element);

            if (virtInfo == null)
            {
                virtInfo = ItemsRepeater.CreateAndInitializeVirtualizationInfo(element);
            }
            else
            {
                // View obtained from ElementFactory already has a VirtualizationInfo attached to it
                // which means that the element has been recycled and not created from scratch.
            }

            if (!itemsSourceContainsElements)
            {
                // Set data context only if no x:Bind was used. ie. No data template component on the root.
                var elementAsFE = element as FrameworkElement;
                elementAsFE.DataContext = data;
            }

            virtInfo.MoveOwnershipToLayoutFromElementFactory(
                index,
                /* uniqueId: */
                m_owner.ItemsSourceView.HasKeyIndexMapping ?
                m_owner.ItemsSourceView.KeyFromIndex(index) :
                string.Empty);

            // The view generator is the only provider that prepares the element.
            var repeater = m_owner;

            // Add the element to the children collection here before raising OnElementPrepared so
            // that handlers can walk up the tree in case they want to find their IndexPath in the
            // nested case.
            var children = repeater.Children;

            if (CachedVisualTreeHelpers.GetParent(element) != repeater)
            {
                children.Add(element);
            }

            repeater.AnimationManager.OnElementPrepared(element);

            repeater.OnElementPrepared(element, index);

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

            return(element);
        }