diff --git a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
index 65f1f0fff2..79780dbd0b 100644
--- a/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
+++ b/src/Avalonia.Controls/ApplicationLifetimes/ClassicDesktopStyleApplicationLifetime.cs
@@ -46,6 +46,10 @@ namespace Avalonia.Controls.ApplicationLifetimes
///
public event EventHandler Startup;
+
+ ///
+ public event EventHandler ShutdownRequested;
+
///
public event EventHandler Exit;
@@ -115,7 +119,7 @@ namespace Avalonia.Controls.ApplicationLifetimes
var lifetimeEvents = AvaloniaLocator.Current.GetService();
if (lifetimeEvents != null)
- lifetimeEvents.ShutdownRequested += ShutdownRequested;
+ lifetimeEvents.ShutdownRequested += OnShutdownRequested;
_cts = new CancellationTokenSource();
MainWindow?.Show();
@@ -130,8 +134,10 @@ namespace Avalonia.Controls.ApplicationLifetimes
_activeLifetime = null;
}
- private void ShutdownRequested(object sender, CancelEventArgs e)
+ private void OnShutdownRequested(object sender, CancelEventArgs e)
{
+ ShutdownRequested?.Invoke(this, e);
+
if (e.Cancel)
return;
diff --git a/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs b/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs
index 212f0b8617..ecf8a0358f 100644
--- a/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs
+++ b/src/Avalonia.Controls/ApplicationLifetimes/IClassicDesktopStyleApplicationLifetime.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
namespace Avalonia.Controls.ApplicationLifetimes
{
@@ -34,5 +35,16 @@ namespace Avalonia.Controls.ApplicationLifetimes
Window MainWindow { get; set; }
IReadOnlyList Windows { get; }
+
+ ///
+ /// Raised by the platform when a shutdown is requested.
+ ///
+ ///
+ /// Raised on on OSX via the Quit menu or right-clicking on the application icon and selecting Quit. This event
+ /// provides a first-chance to cancel application shutdown; if shutdown is not canceled at this point the application
+ /// will try to close each non-owned open window, invoking the event on each and allowing
+ /// each window to cancel the shutdown.
+ ///
+ event EventHandler ShutdownRequested;
}
}
diff --git a/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs b/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs
index 84f02aeda5..38713834c3 100644
--- a/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/DesktopStyleApplicationLifetimeTests.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.ComponentModel;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Platform;
using Avalonia.Threading;
@@ -209,6 +210,33 @@ namespace Avalonia.Controls.UnitTests
Assert.Empty(lifetime.Windows);
}
}
+
+ [Fact]
+ public void Should_Allow_Canceling_Shutdown_Via_ShutdownRequested_Event()
+ {
+ using (UnitTestApplication.Start(TestServices.StyledWindow))
+ using (var lifetime = new ClassicDesktopStyleApplicationLifetime())
+ {
+ var lifetimeEvents = new Mock();
+ AvaloniaLocator.CurrentMutable.Bind().ToConstant(lifetimeEvents.Object);
+ lifetime.Start(Array.Empty());
+
+ var window = new Window();
+ var raised = 0;
+
+ window.Show();
+
+ lifetime.ShutdownRequested += (s, e) =>
+ {
+ e.Cancel = true;
+ ++raised;
+ };
+
+ lifetimeEvents.Raise(x => x.ShutdownRequested += null, new CancelEventArgs());
+
+ Assert.Equal(1, raised);
+ Assert.Equal(new[] { window }, lifetime.Windows);
+ }
+ }
}
-
}