From 3992e0e85514398623abbb9caf844325bfa695e9 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 8 Apr 2019 00:18:20 +0200 Subject: [PATCH 01/20] Added failing test for #2420. --- tests/Avalonia.LeakTests/ControlTests.cs | 33 ++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/Avalonia.LeakTests/ControlTests.cs b/tests/Avalonia.LeakTests/ControlTests.cs index b7aa452a18..cd25b79f1f 100644 --- a/tests/Avalonia.LeakTests/ControlTests.cs +++ b/tests/Avalonia.LeakTests/ControlTests.cs @@ -308,6 +308,39 @@ namespace Avalonia.LeakTests } + [Fact] + public void Slider_Is_Freed() + { + using (Start()) + { + Func run = () => + { + var window = new Window + { + Content = new Slider() + }; + + window.Show(); + + // Do a layout and make sure that Slider gets added to visual tree. + window.LayoutManager.ExecuteInitialLayoutPass(window); + Assert.IsType(window.Presenter.Child); + + // Clear the content and ensure the Slider is removed. + window.Content = null; + window.LayoutManager.ExecuteLayoutPass(); + Assert.Null(window.Presenter.Child); + + return window; + }; + + var result = run(); + + dotMemory.Check(memory => + Assert.Equal(0, memory.GetObjects(where => where.Type.Is()).ObjectsCount)); + } + } + [Fact] public void RendererIsDisposed() { From 1cd900c856a80b370d3dad7e890a2a1654617599 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Mon, 8 Apr 2019 18:39:39 +0200 Subject: [PATCH 02/20] Prevent leak when using TemplateBinding from a Setter. When a `TemplateBinding` is used as a `Setter.Value`, then we need to make sure we don't use the `TemplateBinding` itself as the binding because this can cause a memory leak. Replace `IRequiresTemplateInSetter` with `ISetterValue` which can be used to require a template in the setter for `Control` and can also be used to notify `TemplateBinding` that it's in a setter and so should always clone itself. Fixes #2420 --- src/Avalonia.Controls/Control.cs | 10 +++++++++- .../Styling/IRequiresTemplateInSetter.cs | 10 ---------- src/Avalonia.Styling/Styling/ISetterValue.cs | 13 +++++++++++++ src/Avalonia.Styling/Styling/Setter.cs | 8 +------- .../Avalonia.Markup/Data/TemplateBinding.cs | 17 ++++++++++++----- tests/Avalonia.Styling.UnitTests/SetterTests.cs | 14 ++++++-------- 6 files changed, 41 insertions(+), 31 deletions(-) delete mode 100644 src/Avalonia.Styling/Styling/IRequiresTemplateInSetter.cs create mode 100644 src/Avalonia.Styling/Styling/ISetterValue.cs diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index a7ee027e70..ca5edae4a9 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -1,6 +1,7 @@ // Copyright (c) The Avalonia Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using System; using System.ComponentModel; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; @@ -20,7 +21,7 @@ namespace Avalonia.Controls /// /// - A property to allow user-defined data to be attached to the control. /// - public class Control : InputElement, IControl, INamed, ISupportInitialize, IVisualBrushInitialize, IRequiresTemplateInSetter + public class Control : InputElement, IControl, INamed, ISupportInitialize, IVisualBrushInitialize, ISetterValue { /// /// Defines the property. @@ -90,6 +91,13 @@ namespace Avalonia.Controls /// bool IDataTemplateHost.IsDataTemplatesInitialized => _dataTemplates != null; + /// + void ISetterValue.Initialize(ISetter setter) + { + throw new InvalidOperationException( + "Cannot use a control as a Setter value. Wrap the control in a