Example #1
0
 public ViewManager(ItemsRepeater owner)
 {
     _owner     = owner;
     _resetPool = new UniqueIdElementPool(owner);
 }
Example #2
0
 public PinnedElementInfo(IControl element)
 {
     PinnedElement      = element;
     VirtualizationInfo = ItemsRepeater.GetVirtualizationInfo(element);
 }
Example #3
0
        public void OnItemsSourceChanged(object sender, 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 <= _lastRealizedElementIndexHeldByLayout)
                {
                    _lastRealizedElementIndexHeldByLayout += newCount;
                    foreach (var element in _owner.Children)
                    {
                        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 (var i = 0; i < _pinnedPool.Count; ++i)
                    {
                        var elementInfo = _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         = args.OldItems.Count;
                var newCount         = args.NewItems.Count;
                if (oldStartIndex != newStartingIndex)
                {
                    throw new NotSupportedException("Replace is only allowed with OldStartingIndex equals to NewStartingIndex.");
                }

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

                if (newCount == 0)
                {
                    throw new NotSupportedException("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
                    foreach (var element in _owner.Children)
                    {
                        var virtInfo  = ItemsRepeater.GetVirtualizationInfo(element);
                        var dataIndex = virtInfo.Index;

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

                    EnsureFirstLastRealizedIndices();
                    _lastRealizedElementIndexHeldByLayout += countChange;
                }
                break;
            }

            case NotifyCollectionChangedAction.Remove:
            {
                var oldStartIndex = args.OldStartingIndex;
                var oldCount      = args.OldItems.Count;
                foreach (var element in _owner.Children)
                {
                    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.
                            _owner.ClearElementImpl(element);
                        }
                        else if (dataIndex >= (oldStartIndex + oldCount))
                        {
                            UpdateElementIndex(element, virtInfo, dataIndex - oldCount);
                        }
                    }
                }

                InvalidateRealizedIndicesHeldByLayout();
                break;
            }

            case NotifyCollectionChangedAction.Reset:
                if (_owner.ItemsSourceView.HasKeyIndexMapping)
                {
                    _isDataSourceStableResetPending = true;
                }

                // Walk through all the elements and make sure they are cleared, they will go into
                // the stable id reset pool.
                foreach (var element in _owner.Children)
                {
                    var virtInfo = ItemsRepeater.GetVirtualizationInfo(element);
                    if (virtInfo.IsRealized && virtInfo.AutoRecycleCandidate)
                    {
                        _owner.ClearElementImpl(element);
                    }
                }

                InvalidateRealizedIndicesHeldByLayout();
                break;
            }
        }
Example #4
0
 public ViewportManager(ItemsRepeater owner)
 {
     _owner = owner;
 }
Example #5
0
 public RepeaterLayoutContext(ItemsRepeater owner)
 {
     _owner = owner;
 }
Example #6
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);
        }
 public UniqueIdElementPool(ItemsRepeater owner) => _owner = owner;