Example #1
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 IControl GetElementIfAlreadyHeldByLayout(int index)
        {
            IControl element = null;

            bool cachedFirstLastIndicesInvalid   = _firstRealizedElementIndexHeldByLayout == FirstRealizedElementIndexDefault;
            bool isRequestedIndexInRealizedRange = (_firstRealizedElementIndexHeldByLayout <= index && index <= _lastRealizedElementIndexHeldByLayout);

            if (cachedFirstLastIndicesInvalid || isRequestedIndexInRealizedRange)
            {
                foreach (var child in _owner.Children)
                {
                    var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(child);
                    if (virtInfo?.IsHeldByLayout == true)
                    {
                        // Only give back elements held by layout. If someone else is holding it, they will be served by other methods.
                        int childIndex = virtInfo.Index;
                        _firstRealizedElementIndexHeldByLayout = Math.Min(_firstRealizedElementIndexHeldByLayout, childIndex);
                        _lastRealizedElementIndexHeldByLayout  = Math.Max(_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);
        }
Example #2
0
        private IControl GetElementFromElementFactory(int index)
        {
            // The view generator is the provider of last resort.

            var itemTemplateFactory = _owner.ItemTemplateShim;

            if (itemTemplateFactory == null)
            {
                // If no ItemTemplate was provided, use a default
                var factory = FuncDataTemplate.Default;
                _owner.ItemTemplate = factory;
                itemTemplateFactory = _owner.ItemTemplateShim;
            }

            var data    = _owner.ItemsSourceView.GetAt(index);
            var element = itemTemplateFactory.GetElement(_owner, data);

            var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(element);

            if (virtInfo == null)
            {
                virtInfo = ItemsRepeater.CreateAndInitializeVirtualizationInfo(element);
            }

            // Prepare the element
            element.DataContext = data;

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

            // The view generator is the only provider that prepares the element.
            var repeater = _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 (element.VisualParent != repeater)
            {
                children.Add(element);
            }

            repeater.OnElementPrepared(element, index);

            // Update realized indices
            _firstRealizedElementIndexHeldByLayout = Math.Min(_firstRealizedElementIndexHeldByLayout, index);
            _lastRealizedElementIndexHeldByLayout  = Math.Max(_lastRealizedElementIndexHeldByLayout, index);

            return(element);
        }
Example #3
0
        public IControl GetElement(int index, bool forceCreate, bool suppressAutoRecycle)
        {
            var element = forceCreate ? null : GetElementIfAlreadyHeldByLayout(index);

            if (element == null)
            {
                // check if this is the anchor made through repeater in preparation
                // for a bring into view.
                var madeAnchor = _owner.MadeAnchor;
                if (madeAnchor != null)
                {
                    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;
                Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "GetElement: {Index} Not AutoRecycleCandidate:", virtInfo.Index);
            }
            else
            {
                virtInfo.AutoRecycleCandidate = true;
                virtInfo.KeepAlive            = true;
                Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "GetElement: {Index} AutoRecycleCandidate:", virtInfo.Index);
            }

            return(element);
        }
Example #4
0
        IControl FindFocusCandidate(int clearedIndex, out IControl 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;
            IControl nextElement     = null;
            IControl previousElement = null;

            foreach (var child in _owner.Children)
            {
                var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(child);
                if (virtInfo?.IsHeldByLayout == true)
                {
                    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;
                        }
                    }
                }
            }

            // TODO: 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.
            focusedChild = nextElement;
            return(nextElement);
        }
Example #5
0
        // There are several cases handled here with respect to which element gets returned and when DataContext is modified.
        //
        // 1. If there is no ItemTemplate:
        //    1.1 If data is an IControl -> the data is returned
        //    1.2 If data is not an IControl -> a default DataTemplate is used to fetch element and DataContext is set to data
        //
        // 2. If there is an ItemTemplate:
        //    2.1 If data is not an IControl -> Element is fetched from ElementFactory and DataContext is set to the data
        //    2.2 If data is an IControl:
        //        2.2.1 If Element returned by the ElementFactory is the same as the data -> Element (a.k.a. data) is returned as is
        //        2.2.2 If Element returned by the ElementFactory is not the same as the data
        //                 -> Element that is fetched from the ElementFactory is returned and
        //                    DataContext is set to the data's DataContext (if it exists), otherwise it is set to the data itself
        private IControl GetElementFromElementFactory(int index)
        {
            // The view generator is the provider of last resort.
            var data = _owner.ItemsSourceView !.GetAt(index);
            var providedElementFactory = _owner.ItemTemplateShim;

            IElementFactory GetElementFactory()
            {
                if (providedElementFactory == null)
                {
                    var factory = FuncDataTemplate.Default;
                    _owner.ItemTemplate = factory;
                    return(_owner.ItemTemplateShim !);
                }

                return(providedElementFactory);
            }

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

                var elementFactory = GetElementFactory();
                var args           = _elementFactoryGetArgs ??= new ElementFactoryGetArgs();

                try
                {
                    args.Data   = data;
                    args.Parent = _owner;
                    args.Index  = index;
                    return(elementFactory.GetElement(args));
                }
                finally
                {
                    args.Data   = null;
                    args.Parent = null;
                }
            }

            var element = GetElement();

            var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(element);

            if (virtInfo == null)
            {
                virtInfo = ItemsRepeater.CreateAndInitializeVirtualizationInfo(element);
            }
            // Clear flag
            virtInfo.MustClearDataContext = false;

            if (data != element)
            {
                // Prepare the element
                element.DataContext           = data;
                virtInfo.MustClearDataContext = true;
            }

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

            // The view generator is the only provider that prepares the element.
            var repeater = _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 (element.VisualParent != repeater)
            {
                children.Add(element);
            }

            repeater.OnElementPrepared(element, virtInfo);

            // Update realized indices
            _firstRealizedElementIndexHeldByLayout = Math.Min(_firstRealizedElementIndexHeldByLayout, index);
            _lastRealizedElementIndexHeldByLayout  = Math.Max(_lastRealizedElementIndexHeldByLayout, index);

            return(element);
        }