Skip to content

Fix desktop lifetime non-mainwindow cancellation #17059

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Oct 11, 2024
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using Avalonia.Collections;
Expand Down Expand Up @@ -184,19 +183,24 @@ private bool DoShutdown(
// When an OS shutdown request is received, try to close all non-owned windows. Windows can cancel
// shutdown by setting e.Cancel = true in the Closing event. Owned windows will be shutdown by their
// owners.
foreach (var w in Windows.ToArray())
foreach (var w in new List<Window>(_windows))
{
if (w.Owner is null)
{
w.CloseCore(WindowCloseReason.ApplicationShutdown, isProgrammatic);
}
}

if (!force && Windows.Count > 0)
if (!force)
{
e.Cancel = true;
shutdownCancelled = true;
return false;
if (ShutdownMode == ShutdownMode.OnMainWindowClose ?
MainWindow?.IsVisible == true :
Windows.Count > 0)
{
e.Cancel = true;
shutdownCancelled = true;
return false;
}
}

var args = new ControlledApplicationLifetimeExitEventArgs(exitCode);
Expand Down
4 changes: 3 additions & 1 deletion src/Avalonia.Controls/Window.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1054,10 +1054,12 @@ protected sealed override Size ArrangeSetBounds(Size size)

private protected sealed override void HandleClosed()
{
RaiseEvent(new RoutedEventArgs(WindowClosedEvent));
_shown = false;

base.HandleClosed();

RaiseEvent(new RoutedEventArgs(WindowClosedEvent));

Owner = null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,62 @@ public void Should_Exit_After_MainWindow_Closed()
}
}

[Fact]
public void OnMainWindowClose_Overrides_Secondary_Window_Cancellation()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
using(var lifetime = new ClassicDesktopStyleApplicationLifetime())
{
lifetime.ShutdownMode = ShutdownMode.OnMainWindowClose;
lifetime.SetupCore(Array.Empty<string>());

var hasExit = false;

lifetime.Exit += (_, _) => hasExit = true;

var mainWindow = new Window();
mainWindow.Show();

lifetime.MainWindow = mainWindow;

var window = new Window();
window.Closing += (_, args) => { args.Cancel = true; };
window.Show();

mainWindow.Close();

Assert.True(hasExit);
}
}

[Fact]
public void OnMainWindowClose_Overrides_Secondary_Window_Cancellation_From_TryShutdown()
{
using (UnitTestApplication.Start(TestServices.StyledWindow))
using(var lifetime = new ClassicDesktopStyleApplicationLifetime())
{
lifetime.ShutdownMode = ShutdownMode.OnMainWindowClose;
lifetime.SetupCore(Array.Empty<string>());

var hasExit = false;

lifetime.Exit += (_, _) => hasExit = true;

var mainWindow = new Window();
mainWindow.Show();

lifetime.MainWindow = mainWindow;

var window = new Window();
window.Closing += (_, args) => { args.Cancel = true; };
window.Show();

lifetime.TryShutdown();

Assert.True(hasExit);
}
}

[Fact]
public void Should_Exit_After_Last_Window_Closed()
{
Expand Down
2 changes: 2 additions & 0 deletions tests/Avalonia.Controls.UnitTests/WindowTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ public void IsVisible_Should_Be_False_After_Impl_Signals_Close()
var window = new Window();

window.Show();
Assert.True(window.IsVisible);

windowImpl.Object.Closed();

Assert.False(window.IsVisible);
Expand Down
Loading