From 7c69f3ee0076e1e6de364b384dfeb8ce6a35c3a8 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Tue, 16 Jun 2020 15:51:49 +0200 Subject: [PATCH] Clear DataContext of cleared elements in ItemsRepeater When we set the DataContext, we save that fact in the VirtualizationInformation for that element, when that element gets cleared, we look up in the VirtualizationInformation whether we should need to clear the DataContext too, and do that if necessary. Ported from https://github.com/microsoft/microsoft-ui-xaml/commit/e9bd88e5985397ece3a66541858eab86f2a8e7b0 --- src/Avalonia.Controls/Repeater/ViewManager.cs | 24 ++++++++++++++++--- .../Repeater/VirtualizationInfo.cs | 1 + 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/Repeater/ViewManager.cs b/src/Avalonia.Controls/Repeater/ViewManager.cs index 8906b9f577..ee800cffc4 100644 --- a/src/Avalonia.Controls/Repeater/ViewManager.cs +++ b/src/Avalonia.Controls/Repeater/ViewManager.cs @@ -110,10 +110,28 @@ namespace Avalonia.Controls } } + // We need to clear the datacontext to prevent crashes from happening, + // however we only do that if we were the ones setting it. + // That is when one of the following is the case (numbering taken from line ~642): + // 1.2 No ItemTemplate, data is not a UIElement + // 2.1 ItemTemplate, data is not FrameworkElement + // 2.2.2 Itemtemplate, data is FrameworkElement, ElementFactory returned Element different to data + // + // In all of those three cases, we the ItemTemplateShim is NOT null. + // Luckily when we create the items, we store whether we were the once setting the DataContext. public void ClearElementToElementFactory(IControl element) { _owner.OnElementClearing(element); + var virtInfo = ItemsRepeater.GetVirtualizationInfo(element); + virtInfo.MoveOwnershipToElementFactory(); + + // During creation of this object, we were the one setting the DataContext, so clear it now. + if (virtInfo.MustClearDataContext) + { + element.DataContext = null; + } + if (_owner.ItemTemplateShim != null) { _owner.ItemTemplateShim.RecycleElement(_owner, element); @@ -127,9 +145,6 @@ namespace Avalonia.Controls } } - var virtInfo = ItemsRepeater.GetVirtualizationInfo(element); - virtInfo.MoveOwnershipToElementFactory(); - if (_lastFocusedElement == element) { // Focused element is going away. Remove the tracked last focused element @@ -597,11 +612,14 @@ namespace Avalonia.Controls { virtInfo = ItemsRepeater.CreateAndInitializeVirtualizationInfo(element); } + // Clear flag + virtInfo.MustClearDataContext = false; if (data != element) { // Prepare the element element.DataContext = data; + virtInfo.MustClearDataContext = true; } virtInfo.MoveOwnershipToLayoutFromElementFactory( diff --git a/src/Avalonia.Controls/Repeater/VirtualizationInfo.cs b/src/Avalonia.Controls/Repeater/VirtualizationInfo.cs index eb30c1b7cf..7a639419c1 100644 --- a/src/Avalonia.Controls/Repeater/VirtualizationInfo.cs +++ b/src/Avalonia.Controls/Repeater/VirtualizationInfo.cs @@ -36,6 +36,7 @@ namespace Avalonia.Controls public bool IsHeldByLayout => Owner == ElementOwner.Layout; public bool IsRealized => IsHeldByLayout || Owner == ElementOwner.PinnedPool; public bool IsInUniqueIdResetPool => Owner == ElementOwner.UniqueIdResetPool; + public bool MustClearDataContext { get; set; } public bool KeepAlive { get; set; } public ElementOwner Owner { get; private set; } = ElementOwner.ElementFactory; public string UniqueId { get; private set; }