// 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 a UIElement . the data is returned // 1.2 If data is not a UIElement . 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 a FrameworkElement . Element is fetched from ElementFactory and DataContext is set to the data** // 2.2 If data is a FrameworkElement: // 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** // // **data context is set only if no x:Bind was used. ie. No data template component on the root. UIElement GetElementFromElementFactory(int index) { // The view generator is the provider of last resort. var data = m_owner.ItemsSourceView.GetAt(index); UIElement GetElement() { var elementFactory = m_owner.ItemTemplateShim; if (elementFactory == null) { if (data is UIElement dataAsElement) { return(dataAsElement); } else { // If no ItemTemplate was provided, use a default //var factory = XamlReader.Load("<DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'><TextBlock Text='{Binding}'/></DataTemplate>") as DataTemplate; var factory = new DataTemplate(() => { var tb = new TextBlock(); tb.SetBinding(TextBlock.TextProperty, new Binding()); return(tb); }); m_owner.ItemTemplate = factory; elementFactory = m_owner.ItemTemplateShim; } } if (m_ElementFactoryGetArgs == null) { m_ElementFactoryGetArgs = new ElementFactoryGetArgs(); } var args = m_ElementFactoryGetArgs; using var scopeGuard = Disposable.Create(() => { args.Data = null; args.Parent = null; }); args.Data = data; args.Parent = m_owner; args.Index = index; return(elementFactory.GetElement(args)); }; var element = GetElement(); var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(element); if (virtInfo == null) { virtInfo = ItemsRepeater.CreateAndInitializeVirtualizationInfo(element); REPEATER_TRACE_PERF("ElementCreated"); } 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. REPEATER_TRACE_PERF("ElementRecycled"); } if (data != element) { // Prepare the element // If we are phasing, run phase 0 before setting DataContext. If phase 0 is not // run before setting DataContext, when setting DataContext all the phases will be // run in the OnDataContextChanged handler in code generated by the xaml compiler (code-gen). var extension = CachedVisualTreeHelpers.GetDataTemplateComponent(element); if (extension != null) { // Clear out old data. extension.Recycle(); int nextPhase = VirtualizationInfo.PhaseReachedEnd; // Run Phase 0 extension.ProcessBindings(data, index, 0 /* currentPhase */, out nextPhase); // Setup phasing information, so that Phaser can pick up any pending phases left. // Update phase on virtInfo. Set data and templateComponent only if x:Phase was used. virtInfo.UpdatePhasingInfo(nextPhase, nextPhase > 0 ? data : null, nextPhase > 0 ? extension : null); } else 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. var elementDataContext = data; if (data is FrameworkElement dataAsElement) { var dataDataContext = dataAsElement.DataContext; if (dataDataContext != null) { elementDataContext = dataDataContext; } } elementAsFE.DataContext = elementDataContext; } else { MUX_ASSERT(false, "Element returned by factory is not a FrameworkElement!"); } } virtInfo.MoveOwnershipToLayoutFromElementFactory( index, /* uniqueId: */ m_owner.ItemsSourceView.HasKeyIndexMapping ? m_owner.ItemsSourceView.KeyFromIndex(index) : null); // The view generator is the only provider that prepares the element. var repeater = m_owner; #if IS_UNO //TODO: Uno specific - remove when #4689 is fixed repeater.OnUnoBeforeElementPrepared(element, index); #endif // 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); if (data != element) { m_phaser.PhaseElement(element, virtInfo); } // Update realized indices m_firstRealizedElementIndexHeldByLayout = Math.Min(m_firstRealizedElementIndexHeldByLayout, index); m_lastRealizedElementIndexHeldByLayout = Math.Max(m_lastRealizedElementIndexHeldByLayout, index); return(element); }