public void UpdatePin(UIElement element, bool addPin) { var parent = CachedVisualTreeHelpers.GetParent(element); var child = (DependencyObject)element; while (parent != null) { if (parent is ItemsRepeater repeater) { var virtInfo = ItemsRepeater.GetVirtualizationInfo(child as UIElement); 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 = CachedVisualTreeHelpers.GetParent(child); } }
public void ClearElementToElementFactory(UIElement element) { m_owner.OnElementClearing(element); if (m_owner.ItemTemplateShim != null) { if (m_ElementFactoryRecycleArgs == null) { // Create one. m_ElementFactoryRecycleArgs = new ElementFactoryRecycleArgs(); } var context = m_ElementFactoryRecycleArgs; context.Element = element; context.Parent = m_owner; m_owner.ItemTemplateShim.RecycleElement(context); context.Element = null; context.Parent = null; } else { // No ItemTemplate to recycle to, remove the element from the children collection. var children = m_owner.Children; int childIndex = 0; childIndex = children.IndexOf(element); if (childIndex < 0) { throw new InvalidOperationException("ItemsRepeater's child not found in its Children collection."); } children.RemoveAt(childIndex); } var virtInfo = ItemsRepeater.GetVirtualizationInfo(element); virtInfo.MoveOwnershipToElementFactory(); m_phaser.StopPhasing(element, virtInfo); if (m_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. int clearedIndex = virtInfo.Index; MoveFocusFromClearedIndex(clearedIndex); } REPEATER_TRACE_PERF("ElementCleared"); }
public void Add(UIElement element) { global::System.Diagnostics.Debug.Assert(m_owner.ItemsSourceView.HasKeyIndexMapping); var virtInfo = ItemsRepeater.GetVirtualizationInfo(element); var key = virtInfo.UniqueId; if (m_elementMap.ContainsKey(key)) { throw new InvalidOperationException($"The unique id provided ({virtInfo.UniqueId}) is not unique."); } m_elementMap.Add(key, element); }
void UpdateFocusedElement() { UIElement focusedElement = null; if (FocusManager.GetFocusedElement() is DependencyObject child) { var parent = CachedVisualTreeHelpers.GetParent(child); var owner = (UIElement)m_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 UIElement; if (repeater == owner && ItemsRepeater.GetVirtualizationInfo(element).IsRealized) { focusedElement = element; } break; } child = parent; parent = CachedVisualTreeHelpers.GetParent(child); } } // If the focused element has changed, // we need to unpin the old one and pin the new one. if (m_lastFocusedElement != focusedElement) { if (m_lastFocusedElement != null) { UpdatePin(m_lastFocusedElement, false /* addPin */); } if (focusedElement != null) { UpdatePin(focusedElement, true /* addPin */); } m_lastFocusedElement = focusedElement; } }
public ChildrenInTabFocusOrderIterator(ItemsRepeater repeater) { var children = repeater.Children; m_realizedChildren = new List <KeyValuePair <int, UIElement> >(children.Count); // Filter out unrealized children. for (var i = 0; i < children.Count; ++i) { var element = children[i]; var virtInfo = ItemsRepeater.GetVirtualizationInfo(element); if (virtInfo.IsRealized) { m_realizedChildren.Add(new KeyValuePair <int, UIElement>(virtInfo.Index, element)); } } // Sort children by index. m_realizedChildren.Sort((lhs, rhs) => lhs.Key - rhs.Key); }
public void ClearElement(UIElement element, bool isClearedDueToCollectionChange) { var virtInfo = ItemsRepeater.GetVirtualizationInfo(element); int index = virtInfo.Index; bool cleared = ClearElementToUniqueIdResetPool(element, virtInfo) || ClearElementToAnimator(element, virtInfo) || ClearElementToPinnedPool(element, virtInfo, isClearedDueToCollectionChange); if (!cleared) { ClearElementToElementFactory(element); } // Both First and Last indices need to be valid or default. MUX_ASSERT((m_firstRealizedElementIndexHeldByLayout == FirstRealizedElementIndexDefault && m_lastRealizedElementIndexHeldByLayout == LastRealizedElementIndexDefault) || (m_firstRealizedElementIndexHeldByLayout != FirstRealizedElementIndexDefault && m_lastRealizedElementIndexHeldByLayout != LastRealizedElementIndexDefault)); if (index == m_firstRealizedElementIndexHeldByLayout && index == m_lastRealizedElementIndexHeldByLayout) { // First and last were pointing to the same element and that is going away. InvalidateRealizedIndicesHeldByLayout(); } else if (index == m_firstRealizedElementIndexHeldByLayout) { // The FirstElement is going away, shrink the range by one. ++m_firstRealizedElementIndexHeldByLayout; } else if (index == m_lastRealizedElementIndexHeldByLayout) { // Last element is going away, shrink the range by one at the end. --m_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. } }
UIElement GetElementFromUniqueIdResetPool(int index) { UIElement element = null; // See if you can get it from the reset pool. if (m_isDataSourceStableResetPending) { element = m_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); // Update realized indices m_firstRealizedElementIndexHeldByLayout = Math.Min(m_firstRealizedElementIndexHeldByLayout, index); m_lastRealizedElementIndexHeldByLayout = Math.Max(m_lastRealizedElementIndexHeldByLayout, index); } } return(element); }
public void OnItemsSourceChanged(object _, 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 <= m_lastRealizedElementIndexHeldByLayout) { m_lastRealizedElementIndexHeldByLayout += newCount; var children = m_owner.Children; var childCount = children.Count; for (int i = 0; i < childCount; ++i) { var element = children[i]; 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 (int i = 0; i < m_pinnedPool.Count; ++i) { var elementInfo = m_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 = (int)(args.OldItems.Count); var newCount = (int)(args.NewItems.Count); if (oldStartIndex != newStartingIndex) { throw new InvalidOperationException("Replace is only allowed with OldStartingIndex equals to NewStartingIndex."); } if (oldCount == 0) { throw new InvalidOperationException("Replace notification with args.OldItemsCount value of 0 is not allowed. Use Insert action instead."); } if (newCount == 0) { throw new InvalidOperationException("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 var children = m_owner.Children; for (int i = 0; i < children.Count; ++i) { var element = children[i]; var virtInfo = ItemsRepeater.GetVirtualizationInfo(element); var dataIndex = virtInfo.Index; if (virtInfo.IsRealized) { if (dataIndex >= oldStartIndex + oldCount) { UpdateElementIndex(element, virtInfo, dataIndex + countChange); } } } EnsureFirstLastRealizedIndices(); m_lastRealizedElementIndexHeldByLayout += countChange; } break; } case NotifyCollectionChangedAction.Remove: { var oldStartIndex = args.OldStartingIndex; var oldCount = (int)(args.OldItems.Count); var children = m_owner.Children; for (int i = 0; i < children.Count; ++i) { var element = children[i]; 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. m_owner.ClearElementImpl(element); } else if (dataIndex >= (oldStartIndex + oldCount)) { UpdateElementIndex(element, virtInfo, dataIndex - oldCount); } } } InvalidateRealizedIndicesHeldByLayout(); break; } case NotifyCollectionChangedAction.Reset: // If we get multiple resets back to back before // running layout, we dont have to clear all the elements again. if (!m_isDataSourceStableResetPending) { #if DEBUG // There should be no elements in the reset pool at this time. MUX_ASSERT(m_resetPool.IsEmpty); #endif if (m_owner.ItemsSourceView.HasKeyIndexMapping) { m_isDataSourceStableResetPending = true; } // Walk through all the elements and make sure they are cleared, they will go into // the stable id reset pool. var children = m_owner.Children; for (int i = 0; i < children.Count; ++i) { var element = children[i]; var virtInfo = ItemsRepeater.GetVirtualizationInfo(element); if (virtInfo.IsRealized && virtInfo.AutoRecycleCandidate) { m_owner.ClearElementImpl(element); } } } InvalidateRealizedIndicesHeldByLayout(); break; } }
public PinnedElementInfo(UIElement element) { m_pinnedElement = element; m_virtInfo = ItemsRepeater.GetVirtualizationInfo(element); }