public UIElement GetElement(int index, bool forceCreate, bool suppressAutoRecycle) { UIElement element = forceCreate ? null : GetElementIfAlreadyHeldByLayout(index); if (element == null) { // check if this is the anchor made through repeater in preparation // for a bring into view. if (m_owner.MadeAnchor is UIElement madeAnchor) { var anchorVirtInfo = ItemsRepeater.TryGetVirtualizationInfo(madeAnchor); if (anchorVirtInfo.Index == index) { element = madeAnchor; } } } if (element == null) { element = GetElementFromUniqueIdResetPool(index); } ; if (element == null) { element = GetElementFromPinnedElements(index); } if (element == null) { element = GetElementFromElementFactory(index); } var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(element); if (suppressAutoRecycle) { virtInfo.AutoRecycleCandidate = false; } else { virtInfo.AutoRecycleCandidate = true; virtInfo.KeepAlive = true; } return(element); }
// We optimize for the case where index is not realized to return null as quickly as we can. // Flow layouts manage containers on their own and will never ask for an index that is already realized. // If an index that is realized is requested by the layout, we unfortunately have to walk the // children. Not ideal, but a reasonable default to provide consistent behavior between virtualizing // and non-virtualizing hosts. private UIElement GetElementIfAlreadyHeldByLayout(int index) { UIElement element = null; bool cachedFirstLastIndicesInvalid = m_firstRealizedElementIndexHeldByLayout == FirstRealizedElementIndexDefault; Debug.Assert(!cachedFirstLastIndicesInvalid || m_lastRealizedElementIndexHeldByLayout == LastRealizedElementIndexDefault); bool isRequestedIndexInRealizedRange = (m_firstRealizedElementIndexHeldByLayout <= index && index <= m_lastRealizedElementIndexHeldByLayout); if (cachedFirstLastIndicesInvalid || isRequestedIndexInRealizedRange) { // 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)); var children = m_owner.Children; for (int i = 0; i < children.Count; ++i) { var child = children[i]; var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(child); if (virtInfo != null && virtInfo.IsHeldByLayout) { // Only give back elements held by layout. If someone else is holding it, they will be served by other methods. int childIndex = virtInfo.Index; m_firstRealizedElementIndexHeldByLayout = Math.Min(m_firstRealizedElementIndexHeldByLayout, childIndex); m_lastRealizedElementIndexHeldByLayout = Math.Max(m_lastRealizedElementIndexHeldByLayout, childIndex); if (virtInfo.Index == index) { element = child; // If we have valid first/last indices, we don't have to walk the rest, but if we // do not, then we keep walking through the entire children collection to get accurate // indices once. if (!cachedFirstLastIndicesInvalid) { break; } } } } } return(element); }
private UIElement FindFocusCandidate(int clearedIndex, ref UIElement focusedChild) { // Walk through all the children and find elements with index before and after the cleared index. // Note that during a delete the next element would now have the same index. int previousIndex = int.MinValue; int nextIndex = int.MaxValue; UIElement nextElement = null; UIElement previousElement = null; var children = m_owner.Children; for (int i = 0; i < children.Count; ++i) { var child = children[i]; var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(child); if (virtInfo != null && virtInfo.IsHeldByLayout) { int currentIndex = virtInfo.Index; if (currentIndex < clearedIndex) { if (currentIndex > previousIndex) { previousIndex = currentIndex; previousElement = child; } } else if (currentIndex >= clearedIndex) { // Note that we use >= above because if we deleted the focused element, // the next element would have the same index now. if (currentIndex < nextIndex) { nextIndex = currentIndex; nextElement = child; } } } } // Find the next element if one exists, if not use the previous element. // If the container itself is not focusable, find a descendent that is. UIElement focusCandidate = null; if (nextElement != null) { focusedChild = nextElement as UIElement; if (nextElement.Focus()) { focusCandidate = nextElement; } else if (nextElement.MoveFocus(new TraversalRequest(FocusNavigationDirection.First))) { focusCandidate = FocusManager.GetFocusedElement(nextElement) as UIElement; } } if (focusCandidate == null && previousElement != null) { focusedChild = previousElement as UIElement; if (previousElement.Focus()) { focusCandidate = previousElement; } else if (previousElement.MoveFocus(new TraversalRequest(FocusNavigationDirection.Last))) { focusCandidate = FocusManager.GetFocusedElement(previousElement) as UIElement; } } return(focusCandidate); }
private UIElement GetElementFromElementFactory(int index) { // The view generator is the provider of last resort. var data = m_owner.ItemsSourceView.GetAt(index); UIElement initElement() { var providedElementFactory = m_owner.ItemTemplateShim; if (providedElementFactory == null) { if (data is UIElement dataAsElement) { return(dataAsElement); } } IElementFactoryShim initElementFactory() { if (providedElementFactory == null) { var factory = XamlReader.Parse("<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'><TextBlock Text='{Binding}'/></DataTemplate>") as DataTemplate; m_owner.ItemTemplate = factory; return(m_owner.ItemTemplateShim); } return(providedElementFactory); } var elementFactory = initElementFactory(); ElementFactoryGetArgs initArgs() { if (m_ElementFactoryGetArgs == null) { m_ElementFactoryGetArgs = new ElementFactoryGetArgs(); } return(m_ElementFactoryGetArgs); } var args = initArgs(); try { args.Data = data; args.Parent = m_owner; args.Index = index; return(elementFactory.GetElement(args)); } finally { args.Data = null; args.Parent = null; } } var element = initElement(); var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(element); if (virtInfo == null) { virtInfo = ItemsRepeater.CreateAndInitializeVirtualizationInfo(element); } else { // View obtained from ElementFactory already has a VirtualizationInfo attached to it // which means that the element has been recycled and not created from scratch. } if (data != element) { if (element is FrameworkElement elementAsFE) { // Set data context only if no x:Bind was used. ie. No data template component on the root. // If the passed in data is a UIElement and is different from the element returned by // the template factory then we need to propagate the DataContext. // Otherwise just set the DataContext on the element as the data. object initElementDataContext() { if (data is FrameworkElement dataAsElement) { var dataDataContext = dataAsElement.DataContext; if (dataDataContext != null) { return(dataDataContext); } } return(data); } var elementDataContext = initElementDataContext(); elementAsFE.DataContext = elementDataContext; } else { Debug.Assert(false, "Element returned by factory is not a FrameworkElement!"); } } virtInfo.MoveOwnershipToLayoutFromElementFactory( index, /* uniqueId: */ m_owner.ItemsSourceView.HasKeyIndexMapping ? m_owner.ItemsSourceView.KeyFromIndex(index) : string.Empty); // The view generator is the only provider that prepares the element. var repeater = m_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 (CachedVisualTreeHelpers.GetParent(element) != repeater) { children.Add(element); } repeater.AnimationManager.OnElementPrepared(element); repeater.OnElementPrepared(element, index); // Update realized indices m_firstRealizedElementIndexHeldByLayout = Math.Min(m_firstRealizedElementIndexHeldByLayout, index); m_lastRealizedElementIndexHeldByLayout = Math.Max(m_lastRealizedElementIndexHeldByLayout, index); return(element); }
private UIElement GetElementFromElementFactory(int index) { // The view generator is the provider of last resort. var data = m_owner.ItemsSourceView.GetAt(index); var itemTemplateFactory = m_owner.ItemTemplateShim; UIElement element = null; bool itemsSourceContainsElements = false; if (itemTemplateFactory == null) { element = data as UIElement; // No item template provided and ItemsSource contains objects derived from UIElement. // In this case, just use the data directly as elements. itemsSourceContainsElements = element != null; } if (element == null) { if (itemTemplateFactory == null) { // If no ItemTemplate was provided, use a default var factory = XamlReader.Parse("<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'><TextBlock Text='{Binding}'/></DataTemplate>") as DataTemplate; m_owner.ItemTemplate = factory; itemTemplateFactory = m_owner.ItemTemplateShim; } if (m_ElementFactoryGetArgs == null) { // Create one. m_ElementFactoryGetArgs = new ElementFactoryGetArgs(); } var args = m_ElementFactoryGetArgs; args.Data = data; args.Parent = m_owner; args.Index = index; element = itemTemplateFactory.GetElement(args); args.Data = null; args.Parent = null; } var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(element); if (virtInfo == null) { virtInfo = ItemsRepeater.CreateAndInitializeVirtualizationInfo(element); } else { // View obtained from ElementFactory already has a VirtualizationInfo attached to it // which means that the element has been recycled and not created from scratch. } if (!itemsSourceContainsElements) { // Set data context only if no x:Bind was used. ie. No data template component on the root. var elementAsFE = element as FrameworkElement; elementAsFE.DataContext = data; } virtInfo.MoveOwnershipToLayoutFromElementFactory( index, /* uniqueId: */ m_owner.ItemsSourceView.HasKeyIndexMapping ? m_owner.ItemsSourceView.KeyFromIndex(index) : string.Empty); // The view generator is the only provider that prepares the element. var repeater = m_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 (CachedVisualTreeHelpers.GetParent(element) != repeater) { children.Add(element); } repeater.AnimationManager.OnElementPrepared(element); repeater.OnElementPrepared(element, index); // Update realized indices m_firstRealizedElementIndexHeldByLayout = Math.Min(m_firstRealizedElementIndexHeldByLayout, index); m_lastRealizedElementIndexHeldByLayout = Math.Max(m_lastRealizedElementIndexHeldByLayout, index); return(element); }