public void UpdatePin(IControl element, bool addPin) { var parent = element.VisualParent; var child = (IVisual)element; while (parent != null) { if (parent is ItemsRepeater repeater) { var virtInfo = ItemsRepeater.GetVirtualizationInfo((IControl)child); 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 = child.VisualParent; } }
public void ClearElementToElementFactory(IControl element) { _owner.OnElementClearing(element); if (_owner.ItemTemplateShim != null) { _owner.ItemTemplateShim.RecycleElement(_owner, element); } else { // No ItemTemplate to recycle to, remove the element from the children collection. if (!_owner.Children.Remove(element)) { throw new InvalidOperationException("ItemsRepeater's child not found in its Children collection."); } } var virtInfo = ItemsRepeater.GetVirtualizationInfo(element); virtInfo.MoveOwnershipToElementFactory(); if (_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. MoveFocusFromClearedIndex(virtInfo.Index); } }
private void UpdateFocusedElement() { IControl?focusedElement = null; var child = FocusManager.Instance?.Current; if (child != null) { var parent = child.VisualParent; var owner = _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 IControl; if (repeater == owner && element is not null && ItemsRepeater.GetVirtualizationInfo(element).IsRealized) { focusedElement = element; } break; } child = parent as IInputElement; parent = child?.VisualParent; } } // If the focused element has changed, // we need to unpin the old one and pin the new one. if (_lastFocusedElement != focusedElement) { if (_lastFocusedElement != null) { UpdatePin(_lastFocusedElement, false /* addPin */); } if (focusedElement != null) { UpdatePin(focusedElement, true /* addPin */); } _lastFocusedElement = focusedElement; } }
public void ClearElementToElementFactory(IControl element) { var virtInfo = ItemsRepeater.GetVirtualizationInfo(element); var clearedIndex = virtInfo.Index; _owner.OnElementClearing(element); _owner.ItemTemplateShim.RecycleElement(_owner, element); virtInfo.MoveOwnershipToElementFactory(); if (_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. MoveFocusFromClearedIndex(clearedIndex); } }
private IControl GetElementFromUniqueIdResetPool(int index) { IControl element = null; // See if you can get it from the reset pool. if (_isDataSourceStableResetPending) { element = _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); } } return(element); }
// We need to clear the datacontext to prevent crashes from happening, // however we only do that if we were the ones setting it. // That is when one of the following is the case (numbering taken from line ~642): // 1.2 No ItemTemplate, data is not a UIElement // 2.1 ItemTemplate, data is not FrameworkElement // 2.2.2 Itemtemplate, data is FrameworkElement, ElementFactory returned Element different to data // // In all of those three cases, we the ItemTemplateShim is NOT null. // Luckily when we create the items, we store whether we were the once setting the DataContext. public void ClearElementToElementFactory(IControl element) { _owner.OnElementClearing(element); var virtInfo = ItemsRepeater.GetVirtualizationInfo(element); virtInfo.MoveOwnershipToElementFactory(); // During creation of this object, we were the one setting the DataContext, so clear it now. if (virtInfo.MustClearDataContext) { element.DataContext = null; } if (_owner.ItemTemplateShim != null) { var context = _elementFactoryRecycleArgs ??= new ElementFactoryRecycleArgs(); context.Element = element; context.Parent = _owner; _owner.ItemTemplateShim.RecycleElement(context); context.Element = null; context.Parent = null; } else { // No ItemTemplate to recycle to, remove the element from the children collection. if (!_owner.Children.Remove(element)) { throw new InvalidOperationException("ItemsRepeater's child not found in its Children collection."); } } if (_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. MoveFocusFromClearedIndex(virtInfo.Index); } }
public void ClearElement(IControl element, bool isClearedDueToCollectionChange) { var virtInfo = ItemsRepeater.GetVirtualizationInfo(element); var index = virtInfo.Index; bool cleared = ClearElementToUniqueIdResetPool(element, virtInfo) || ClearElementToPinnedPool(element, virtInfo, isClearedDueToCollectionChange); if (!cleared) { ClearElementToElementFactory(element); } // Both First and Last indices need to be valid or default. if (index == _firstRealizedElementIndexHeldByLayout && index == _lastRealizedElementIndexHeldByLayout) { // First and last were pointing to the same element and that is going away. InvalidateRealizedIndicesHeldByLayout(); } else if (index == _firstRealizedElementIndexHeldByLayout) { // The FirstElement is going away, shrink the range by one. ++_firstRealizedElementIndexHeldByLayout; } else if (index == _lastRealizedElementIndexHeldByLayout) { // Last element is going away, shrink the range by one at the end. --_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. } }
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; } }