// 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 IControl GetElementIfAlreadyHeldByLayout(int index) { IControl element = null; bool cachedFirstLastIndicesInvalid = _firstRealizedElementIndexHeldByLayout == FirstRealizedElementIndexDefault; bool isRequestedIndexInRealizedRange = (_firstRealizedElementIndexHeldByLayout <= index && index <= _lastRealizedElementIndexHeldByLayout); if (cachedFirstLastIndicesInvalid || isRequestedIndexInRealizedRange) { foreach (var child in _owner.Children) { var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(child); if (virtInfo?.IsHeldByLayout == true) { // Only give back elements held by layout. If someone else is holding it, they will be served by other methods. int childIndex = virtInfo.Index; _firstRealizedElementIndexHeldByLayout = Math.Min(_firstRealizedElementIndexHeldByLayout, childIndex); _lastRealizedElementIndexHeldByLayout = Math.Max(_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 IControl GetElementFromElementFactory(int index) { // The view generator is the provider of last resort. var itemTemplateFactory = _owner.ItemTemplateShim; if (itemTemplateFactory == null) { // If no ItemTemplate was provided, use a default var factory = FuncDataTemplate.Default; _owner.ItemTemplate = factory; itemTemplateFactory = _owner.ItemTemplateShim; } var data = _owner.ItemsSourceView.GetAt(index); var element = itemTemplateFactory.GetElement(_owner, data); var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(element); if (virtInfo == null) { virtInfo = ItemsRepeater.CreateAndInitializeVirtualizationInfo(element); } // Prepare the element element.DataContext = data; 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, index); // Update realized indices _firstRealizedElementIndexHeldByLayout = Math.Min(_firstRealizedElementIndexHeldByLayout, index); _lastRealizedElementIndexHeldByLayout = Math.Max(_lastRealizedElementIndexHeldByLayout, index); return(element); }
public IControl GetElement(int index, bool forceCreate, bool suppressAutoRecycle) { var element = forceCreate ? null : GetElementIfAlreadyHeldByLayout(index); if (element == null) { // check if this is the anchor made through repeater in preparation // for a bring into view. var madeAnchor = _owner.MadeAnchor; if (madeAnchor != null) { 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; Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "GetElement: {Index} Not AutoRecycleCandidate:", virtInfo.Index); } else { virtInfo.AutoRecycleCandidate = true; virtInfo.KeepAlive = true; Logger.TryGet(LogEventLevel.Verbose, "Repeater")?.Log(this, "GetElement: {Index} AutoRecycleCandidate:", virtInfo.Index); } return(element); }
IControl FindFocusCandidate(int clearedIndex, out IControl 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; IControl nextElement = null; IControl previousElement = null; foreach (var child in _owner.Children) { var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(child); if (virtInfo?.IsHeldByLayout == true) { 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; } } } } // TODO: 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. focusedChild = nextElement; return(nextElement); }
// 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); }