public ViewManager(ItemsRepeater owner) { _owner = owner; _resetPool = new UniqueIdElementPool(owner); }
public PinnedElementInfo(IControl element) { PinnedElement = element; VirtualizationInfo = ItemsRepeater.GetVirtualizationInfo(element); }
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; } }
public ViewportManager(ItemsRepeater owner) { _owner = owner; }
public RepeaterLayoutContext(ItemsRepeater owner) { _owner = owner; }
// 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;