From 0236ced64fc31ba998177b05f3cd23623595e723 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 9 Jan 2020 11:32:39 +0100 Subject: [PATCH] Fix ItemsRepeater overwriting DataContext Ported from https://github.com/microsoft/microsoft-ui-xaml/commit/0e24e05dbdb231aa0f4e3ec22e13ad528f155eb6 --- src/Avalonia.Controls/Repeater/ViewManager.cs | 49 +++++++++++++------ 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/src/Avalonia.Controls/Repeater/ViewManager.cs b/src/Avalonia.Controls/Repeater/ViewManager.cs index d670aa077c..7d005a30b4 100644 --- a/src/Avalonia.Controls/Repeater/ViewManager.cs +++ b/src/Avalonia.Controls/Repeater/ViewManager.cs @@ -526,43 +526,60 @@ namespace Avalonia.Controls return element; } + // 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 itemTemplateFactory = _owner.ItemTemplateShim; - IControl element = null; - var itemsSourceContainsElements = false; + var providedElementFactory = _owner.ItemTemplateShim; - if (itemTemplateFactory == null) + ItemTemplateWrapper GetElementFactory() { - element = data as IControl; + if (providedElementFactory == null) + { + var factory = FuncDataTemplate.Default; + _owner.ItemTemplate = factory; + return _owner.ItemTemplateShim; + } - // No item template provided and ItemsSource contains objects derived from UIElement. - // In this case, just use the data directly as elements. - itemsSourceContainsElements = element != null; + return providedElementFactory; } - if (element == null) + IControl GetElement() { - if (itemTemplateFactory == null) + if (providedElementFactory == null) { - // If no ItemTemplate was provided, use a default - var factory = FuncDataTemplate.Default; - _owner.ItemTemplate = factory; - itemTemplateFactory = _owner.ItemTemplateShim; + if (data is IControl dataAsElement) + { + return dataAsElement; + } } - element = itemTemplateFactory.GetElement(_owner, data); + var elementFactory = GetElementFactory(); + return elementFactory.GetElement(_owner, data); } + var element = GetElement(); + var virtInfo = ItemsRepeater.TryGetVirtualizationInfo(element); if (virtInfo == null) { virtInfo = ItemsRepeater.CreateAndInitializeVirtualizationInfo(element); } - if (!itemsSourceContainsElements) + if (data != element) { // Prepare the element element.DataContext = data;