@@ -597,49 +597,69 @@ winrt::UIElement ViewManager::GetElementFromPinnedElements(int index)
597597 return element;
598598}
599599
600+ // There are several cases handled here with respect to which element gets returned and when DataContext is modified.
601+ //
602+ // 1. If there is no ItemTemplate:
603+ // 1.1 If data is a UIElement -> the data is returned
604+ // 1.2 If data is not a UIElement -> a default DataTemplate is used to fetch element and DataContext is set to data**
605+ //
606+ // 2. If there is an ItemTemplate:
607+ // 2.1 If data is not a FrameworkElement -> Element is fetched from ElementFactory and DataContext is set to the data**
608+ // 2.2 If data is a FrameworkElement:
609+ // 2.2.1 If Element returned by the ElementFactory is the same as the data -> Element (a.k.a. data) is returned as is
610+ // 2.2.2 If Element returned by the ElementFactory is not the same as the data
611+ // -> Element that is fetched from the ElementFactory is returned and
612+ // DataContext is set to the data's DataContext (if it exists), otherwise it is set to the data itself**
613+ //
614+ // **data context is set only if no x:Bind was used. ie. No data template component on the root.
600615winrt::UIElement ViewManager::GetElementFromElementFactory (int index)
601616{
602617 // The view generator is the provider of last resort.
603- auto data = m_owner->ItemsSourceView ().GetAt (index);
604-
605- auto itemTemplateFactory = m_owner->ItemTemplateShim ();
618+ auto const data = m_owner->ItemsSourceView ().GetAt (index);
606619
607- winrt::UIElement element = nullptr ;
608- bool itemsSourceContainsElements = false ;
609- if (!itemTemplateFactory)
610- {
611- element = data.try_as <winrt::UIElement>();
612- // No item template provided and ItemsSource contains objects derived from UIElement.
613- // In this case, just use the data directly as elements.
614- itemsSourceContainsElements = element != nullptr ;
615- }
616-
617- if (!element)
620+ auto const element = [this , data, index, providedElementFactory = m_owner->ItemTemplateShim ()]()
618621 {
619- if (!itemTemplateFactory )
622+ if (!providedElementFactory )
620623 {
621- // If no ItemTemplate was provided, use a default
622- auto factory = winrt::XamlReader::Load ( L" <DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'><TextBlock Text='{Binding}'/></DataTemplate> " ). as <winrt::DataTemplate>();
623- m_owner-> ItemTemplate (factory) ;
624- itemTemplateFactory = m_owner-> ItemTemplateShim ();
624+ if ( auto const dataAsElement = data. try_as <winrt::UIElement>())
625+ {
626+ return dataAsElement ;
627+ }
625628 }
626629
627- if (!m_ElementFactoryGetArgs )
630+ auto const elementFactory = [ this , providedElementFactory]( )
628631 {
629- // Create one.
630- m_ElementFactoryGetArgs = tracker_ref<winrt::ElementFactoryGetArgs>(m_owner, *winrt::make_self<ElementFactoryGetArgs>());
631- }
632+ if (!providedElementFactory)
633+ {
634+ // If no ItemTemplate was provided, use a default
635+ auto const factory = winrt::XamlReader::Load (L" <DataTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'><TextBlock Text='{Binding}'/></DataTemplate>" ).as <winrt::DataTemplate>();
636+ m_owner->ItemTemplate (factory);
637+ return m_owner->ItemTemplateShim ();
638+ }
639+ return providedElementFactory;
640+ }();
641+
642+ auto const args = [this ]()
643+ {
644+ if (!m_ElementFactoryGetArgs)
645+ {
646+ m_ElementFactoryGetArgs = tracker_ref<winrt::ElementFactoryGetArgs>(m_owner, *winrt::make_self<ElementFactoryGetArgs>());
647+ }
648+ return m_ElementFactoryGetArgs.get ();
649+ }();
650+
651+ auto scopeGuard = gsl::finally ([args]()
652+ {
653+ args.Data (nullptr );
654+ args.Parent (nullptr );
655+ });
632656
633- auto args = m_ElementFactoryGetArgs.get ();
634657 args.Data (data);
635658 args.Parent (*m_owner);
636659 args.as <ElementFactoryGetArgs>()->Index (index);
637660
638- element = itemTemplateFactory.GetElement (args);
639-
640- args.Data (nullptr );
641- args.Parent (nullptr );
642- }
661+ return elementFactory.GetElement (args);
662+ }();
643663
644664 auto virtInfo = ItemsRepeater::TryGetVirtualizationInfo (element);
645665 if (!virtInfo)
@@ -654,14 +674,13 @@ winrt::UIElement ViewManager::GetElementFromElementFactory(int index)
654674 REPEATER_TRACE_PERF (L" ElementRecycled" );
655675 }
656676
657- if (!itemsSourceContainsElements )
677+ if (data != element )
658678 {
659679 // Prepare the element
660680 // If we are phasing, run phase 0 before setting DataContext. If phase 0 is not
661681 // run before setting DataContext, when setting DataContext all the phases will be
662682 // run in the OnDataContextChanged handler in code generated by the xaml compiler (code-gen).
663- auto extension = CachedVisualTreeHelpers::GetDataTemplateComponent (element);
664- if (extension)
683+ if (auto extension = CachedVisualTreeHelpers::GetDataTemplateComponent (element))
665684 {
666685 // Clear out old data.
667686 extension.Recycle ();
@@ -673,11 +692,29 @@ winrt::UIElement ViewManager::GetElementFromElementFactory(int index)
673692 // Update phase on virtInfo. Set data and templateComponent only if x:Phase was used.
674693 virtInfo->UpdatePhasingInfo (nextPhase, nextPhase > 0 ? data : nullptr , nextPhase > 0 ? extension : nullptr );
675694 }
676- else
695+ else if ( auto elementAsFE = element. try_as <winrt::FrameworkElement>())
677696 {
678697 // Set data context only if no x:Bind was used. ie. No data template component on the root.
679- auto elementAsFE = element.try_as <winrt::FrameworkElement>();
680- elementAsFE.DataContext (data);
698+ // If the passed in data is a UIElement and is different from the element returned by
699+ // the template factory then we need to propagate the DataContext.
700+ // Otherwise just set the DataContext on the element as the data.
701+ auto const elementDataContext = [this , data]()
702+ {
703+ if (auto const dataAsElement = data.try_as <winrt::FrameworkElement>())
704+ {
705+ if (auto const dataDataContext = dataAsElement.DataContext ())
706+ {
707+ return dataDataContext;
708+ }
709+ }
710+ return data;
711+ }();
712+
713+ elementAsFE.DataContext (elementDataContext);
714+ }
715+ else
716+ {
717+ MUX_ASSERT (L" Element returned by factory is not a FrameworkElement!" );
681718 }
682719 }
683720
@@ -704,7 +741,7 @@ winrt::UIElement ViewManager::GetElementFromElementFactory(int index)
704741
705742 repeater->OnElementPrepared (element, index);
706743
707- if (!itemsSourceContainsElements )
744+ if (data != element )
708745 {
709746 m_phaser.PhaseElement (element, virtInfo);
710747 }
0 commit comments