From f65f08e4be0a0927dd7bca50b23c96baa5cd5a4a Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 9 Sep 2021 15:28:20 +0100 Subject: [PATCH] implement trayicon lifetime management. --- .../Platform/ITrayIconImpl.cs | 2 +- src/Avalonia.Controls/TrayIcon.cs | 21 ++++++++++++- .../Remote/TrayIconStub.cs | 5 ++++ src/Windows/Avalonia.Win32/TrayIconImpl.cs | 30 +++++++++++++++---- 4 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Controls/Platform/ITrayIconImpl.cs b/src/Avalonia.Controls/Platform/ITrayIconImpl.cs index e1eb62821b..013aff13ee 100644 --- a/src/Avalonia.Controls/Platform/ITrayIconImpl.cs +++ b/src/Avalonia.Controls/Platform/ITrayIconImpl.cs @@ -6,7 +6,7 @@ using Avalonia.Controls.Platform; namespace Avalonia.Platform { - public interface ITrayIconImpl + public interface ITrayIconImpl : IDisposable { /// /// Sets the icon of this tray icon. diff --git a/src/Avalonia.Controls/TrayIcon.cs b/src/Avalonia.Controls/TrayIcon.cs index 8f422dd4d4..dfa71dcb1e 100644 --- a/src/Avalonia.Controls/TrayIcon.cs +++ b/src/Avalonia.Controls/TrayIcon.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using Avalonia.Collections; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.Platform; using Avalonia.Platform; @@ -12,7 +13,7 @@ namespace Avalonia.Controls { } - public class TrayIcon : AvaloniaObject, IDataContextProvider, INativeMenuExporterProvider + public class TrayIcon : AvaloniaObject, IDataContextProvider, INativeMenuExporterProvider, IDisposable { private readonly ITrayIconImpl _impl; @@ -44,6 +45,21 @@ namespace Avalonia.Controls } } }); + + if (Application.Current.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime lifetime) + { + lifetime.Exit += Lifetime_Exit; + } + } + + private static void Lifetime_Exit(object sender, ControlledApplicationLifetimeExitEventArgs e) + { + var trayIcons = GetTrayIcons(Application.Current); + + foreach(var icon in trayIcons) + { + icon.Dispose(); + } } private static void Icons_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) @@ -84,6 +100,7 @@ namespace Avalonia.Controls /// public static readonly StyledProperty IsVisibleProperty = Visual.IsVisibleProperty.AddOwner(); + private bool _disposedValue; public static void SetTrayIcons(AvaloniaObject o, TrayIcons trayIcons) => o.SetValue(TrayIconsProperty, trayIcons); @@ -160,5 +177,7 @@ namespace Avalonia.Controls _impl.SetToolTipText(change.NewValue.GetValueOrDefault()); } } + + public void Dispose() => _impl.Dispose(); } } diff --git a/src/Avalonia.DesignerSupport/Remote/TrayIconStub.cs b/src/Avalonia.DesignerSupport/Remote/TrayIconStub.cs index 13d38660eb..6fd70f203c 100644 --- a/src/Avalonia.DesignerSupport/Remote/TrayIconStub.cs +++ b/src/Avalonia.DesignerSupport/Remote/TrayIconStub.cs @@ -13,6 +13,11 @@ namespace Avalonia.DesignerSupport.Remote public INativeMenuExporter MenuExporter => throw new NotImplementedException(); + public void Dispose() + { + throw new NotImplementedException(); + } + public void SetIcon(IWindowIconImpl icon) { } diff --git a/src/Windows/Avalonia.Win32/TrayIconImpl.cs b/src/Windows/Avalonia.Win32/TrayIconImpl.cs index cdd0c3a2f6..c1286f8436 100644 --- a/src/Windows/Avalonia.Win32/TrayIconImpl.cs +++ b/src/Windows/Avalonia.Win32/TrayIconImpl.cs @@ -25,6 +25,7 @@ namespace Avalonia.Win32 private readonly Win32NativeToManagedMenuExporter _exporter; private static Dictionary s_trayIcons = new Dictionary(); + private bool _disposedValue; public INativeMenuExporter MenuExporter => _exporter; @@ -45,12 +46,6 @@ namespace Avalonia.Win32 s_trayIcons.Add(_uniqueId, this); } - - ~TrayIconImpl() - { - UpdateIcon(false); - } - public void SetIcon(IWindowIconImpl? icon) { _icon = icon as IconImpl; @@ -258,5 +253,28 @@ namespace Avalonia.Win32 public virtual double Scaling => _hiddenWindow.Screens.Primary.PixelDensity; } } + + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + UpdateIcon(true); + + _disposedValue = true; + } + } + + ~TrayIconImpl() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: false); + } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } } }