diff --git a/src/Avalonia.Controls/TopLevel.cs b/src/Avalonia.Controls/TopLevel.cs
index f314629d02..1853d67019 100644
--- a/src/Avalonia.Controls/TopLevel.cs
+++ b/src/Avalonia.Controls/TopLevel.cs
@@ -184,7 +184,7 @@ namespace Avalonia.Controls
///
/// Gets the renderer for the window.
///
- public IRenderer Renderer { get; }
+ public IRenderer Renderer { get; private set; }
///
/// Gets the access key handler for the window.
@@ -381,6 +381,8 @@ namespace Avalonia.Controls
private void HandleClosed()
{
Closed?.Invoke(this, EventArgs.Empty);
+ Renderer?.Dispose();
+ Renderer = null;
_applicationLifecycle.OnExit -= OnApplicationExiting;
}
diff --git a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs
index 10ff735476..aadbe16d1d 100644
--- a/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs
+++ b/src/Gtk/Avalonia.Gtk3/TopLevelImpl.cs
@@ -75,7 +75,7 @@ namespace Avalonia.Gtk3
private bool OnDestroy(IntPtr gtkwidget, IntPtr userdata)
{
- Closed?.Invoke();
+ Dispose();
return false;
}
@@ -210,7 +210,9 @@ namespace Avalonia.Gtk3
public void Dispose()
{
- Closed?.Invoke();
+ //We are calling it here, since signal handler will be detached
+ if (!GtkWidget.IsClosed)
+ Closed?.Invoke();
foreach(var d in Disposables.AsEnumerable().Reverse())
d.Dispose();
Disposables.Clear();
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs
index 5f59413ba2..d9b200169f 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.cs
@@ -182,9 +182,18 @@ namespace Avalonia.Win32
public void Dispose()
{
- s_instances.Remove(this);
- _framebuffer.Dispose();
- UnmanagedMethods.DestroyWindow(_hwnd);
+ _framebuffer?.Dispose();
+ _framebuffer = null;
+ if (_hwnd != IntPtr.Zero)
+ {
+ UnmanagedMethods.DestroyWindow(_hwnd);
+ _hwnd = IntPtr.Zero;
+ }
+ if (_className != null)
+ {
+ UnmanagedMethods.UnregisterClass(_className, UnmanagedMethods.GetModuleHandle(null));
+ _className = null;
+ }
}
public void Hide()
@@ -418,12 +427,13 @@ namespace Avalonia.Win32
return IntPtr.Zero;
case UnmanagedMethods.WindowsMessage.WM_DESTROY:
- if (Closed != null)
- {
- UnmanagedMethods.UnregisterClass(_className, UnmanagedMethods.GetModuleHandle(null));
- Closed();
- }
-
+ //Window doesn't exist anymore
+ _hwnd = IntPtr.Zero;
+ //Remove root reference to this class, so unmanaged delegate can be collected
+ s_instances.Remove(this);
+ Closed?.Invoke();
+ //Free other resources
+ Dispose();
return IntPtr.Zero;
case UnmanagedMethods.WindowsMessage.WM_DPICHANGED:
diff --git a/tests/Avalonia.LeakTests/ControlTests.cs b/tests/Avalonia.LeakTests/ControlTests.cs
index 1e257a698c..23670257ff 100644
--- a/tests/Avalonia.LeakTests/ControlTests.cs
+++ b/tests/Avalonia.LeakTests/ControlTests.cs
@@ -11,6 +11,7 @@ using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Diagnostics;
using Avalonia.Layout;
+using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.Styling;
using Avalonia.UnitTests;
@@ -301,6 +302,33 @@ namespace Avalonia.LeakTests
}
}
+
+ [Fact]
+ public void RendererIsDisposed()
+ {
+ using (UnitTestApplication.Start(TestServices.StyledWindow))
+ {
+ var renderer = new Mock();
+ renderer.Setup(x => x.Dispose());
+ var impl = new Mock();
+ impl.SetupGet(x => x.Scaling).Returns(1);
+ impl.SetupProperty(x => x.Closed);
+ impl.Setup(x => x.Dispose()).Callback(() => impl.Object.Closed());
+
+ AvaloniaLocator.CurrentMutable.Bind()
+ .ToConstant(new MockWindowingPlatform(() => impl.Object));
+ AvaloniaLocator.CurrentMutable.Bind()
+ .ToConstant(new MockRendererFactory(renderer.Object));
+ var window = new Window()
+ {
+ Content = new Button()
+ };
+ window.Show();
+ window.Close();
+ renderer.Verify(r => r.Dispose());
+ }
+ }
+
private static void PurgeMoqReferences()
{
// Moq holds onto references in its mock of IRenderer in case we want to check if a method has been called;
diff --git a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj
index 7a84243953..f7878ab91a 100644
--- a/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj
+++ b/tests/Avalonia.UnitTests/Avalonia.UnitTests.csproj
@@ -55,6 +55,7 @@
+
diff --git a/tests/Avalonia.UnitTests/MockRendererFactory.cs b/tests/Avalonia.UnitTests/MockRendererFactory.cs
new file mode 100644
index 0000000000..e62012b13c
--- /dev/null
+++ b/tests/Avalonia.UnitTests/MockRendererFactory.cs
@@ -0,0 +1,29 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Avalonia.Rendering;
+
+namespace Avalonia.UnitTests
+{
+ public class MockRendererFactory : IRendererFactory
+ {
+ private readonly Func _cb;
+
+ public MockRendererFactory(Func cb = null)
+ {
+ _cb = cb;
+ }
+
+ public MockRendererFactory(IRenderer renderer) : this((_, __) => renderer)
+ {
+
+ }
+
+ public IRenderer CreateRenderer(IRenderRoot root, IRenderLoop renderLoop)
+ {
+ return _cb?.Invoke(root, renderLoop);
+ }
+ }
+}