public void UpdatePin(UIElement element, bool addPin) { var parent = CachedVisualTreeHelpers.GetParent(element); DependencyObject child = element; while (parent != null) { if (parent is ItemsRepeater repeater) { var virtInfo = ItemsRepeater.GetVirtualizationInfo((UIElement)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 = CachedVisualTreeHelpers.GetParent(child); } }
// 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(UIElement element) { m_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) { if (element is FrameworkElement elementAsFE) { elementAsFE.DataContext = null; } } 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; bool found = children.IndexOf(element, out childIndex); if (!found) { throw new Exception("ItemsRepeater's child not found in its Children collection."); } children.RemoveAt(childIndex); } 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); } }
private void UpdateFocusedElement() { UIElement focusedElement = null; // auto child = winrt::FocusManager::GetFocusedElement().as<winrt::DependencyObject>(); var child = Keyboard.FocusedElement as DependencyObject; if (child != null) { var parent = CachedVisualTreeHelpers.GetParent(child); var owner = m_owner; // Find out if the focused element belongs to one of our direct // children. while (parent != null) { var repeater = parent as ItemsRepeater; if (repeater != null) { 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 void Add(UIElement element) { Debug.Assert(m_owner.ItemsSourceView.HasKeyIndexMapping); var virtInfo = ItemsRepeater.GetVirtualizationInfo(element); var key = virtInfo.UniqueId; if (m_elementMap.ContainsKey(key)) { string message = "The unique id provided (" + virtInfo.UniqueId + ") is not unique."; throw new Exception(message); } m_elementMap.Add(key, element); }
private 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); } } return(element); }
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. Debug.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. } }
public PinnedElementInfo(UIElement element) { PinnedElement = element; VirtualizationInfo = ItemsRepeater.GetVirtualizationInfo(element); }
public void OnItemsSourceChanged(object source, 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 = args.OldItems.Count; var newCount = args.NewItems.Count; if (oldStartIndex != newStartingIndex) { throw new Exception("Replace is only allowed with OldStartingIndex equals to NewStartingIndex."); } if (oldCount == 0) { throw new Exception("Replace notification with args.OldItemsCount value of 0 is not allowed. Use Insert action instead."); } if (newCount == 0) { throw new Exception("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 = 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 (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; } } }