diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs index ab99cfad17..98f4cadc13 100644 --- a/src/Avalonia.Controls/Window.cs +++ b/src/Avalonia.Controls/Window.cs @@ -4,10 +4,7 @@ using System.ComponentModel; using System.Linq; using System.Reactive.Linq; using System.Threading.Tasks; -using Avalonia.Controls.Chrome; using Avalonia.Controls.Platform; -using Avalonia.Controls.Primitives; -using Avalonia.Data; using Avalonia.Input; using Avalonia.Interactivity; using Avalonia.Layout; @@ -482,10 +479,9 @@ namespace Avalonia.Controls try { - if (!ignoreCancel && HandleClosing()) + if (!ignoreCancel && ShouldCancelClose()) { close = false; - return; } } finally @@ -497,11 +493,25 @@ namespace Avalonia.Controls } } + /// + /// Handles a closing notification from . + /// true if closing is cancelled. Otherwise false. + /// + protected virtual bool HandleClosing() + { + if (!ShouldCancelClose()) + { + CloseInternal(); + return false; + } + + return true; + } + private void CloseInternal() { foreach (var (child, _) in _children.ToList()) { - // if we HandleClosing() before then there will be no children. child.CloseInternal(); } @@ -515,20 +525,18 @@ namespace Avalonia.Controls PlatformImpl?.Dispose(); } - /// - /// Handles a closing notification from . - /// - protected virtual bool HandleClosing() + private bool ShouldCancelClose(CancelEventArgs args = null) { + if (args is null) + { + args = new CancelEventArgs(); + } + bool canClose = true; foreach (var (child, _) in _children.ToList()) { - if (!child.HandleClosing()) - { - child.CloseInternal(); - } - else + if (child.ShouldCancelClose(args)) { canClose = false; } @@ -536,15 +544,12 @@ namespace Avalonia.Controls if (canClose) { - var args = new CancelEventArgs(); OnClosing(args); return args.Cancel; } - else - { - return !canClose; - } + + return true; } protected virtual void HandleWindowStateChanged(WindowState state) diff --git a/tests/Avalonia.Controls.UnitTests/WindowTests.cs b/tests/Avalonia.Controls.UnitTests/WindowTests.cs index ba29001cf3..e8311b79ac 100644 --- a/tests/Avalonia.Controls.UnitTests/WindowTests.cs +++ b/tests/Avalonia.Controls.UnitTests/WindowTests.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Avalonia.Layout; using Avalonia.Platform; using Avalonia.Rendering; using Avalonia.UnitTests; @@ -137,6 +136,129 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(1, count); } } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void Child_windows_should_be_closed_before_parent(bool programaticClose) + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var window = new Window(); + var child = new Window(); + + int count = 0; + int windowClosing = 0; + int childClosing = 0; + int windowClosed = 0; + int childClosed = 0; + + window.Closing += (sender, e) => + { + count++; + windowClosing = count; + }; + + child.Closing += (sender, e) => + { + count++; + childClosing = count; + }; + + window.Closed += (sender, e) => + { + count++; + windowClosed = count; + }; + + child.Closed += (sender, e) => + { + count++; + childClosed = count; + }; + + window.Show(); + child.Show(window); + + if (programaticClose) + { + window.Close(); + } + else + { + var cancel = window.PlatformImpl.Closing(); + + Assert.Equal(false, cancel); + } + + Assert.Equal(2, windowClosing); + Assert.Equal(1, childClosing); + Assert.Equal(4, windowClosed); + Assert.Equal(3, childClosed); + } + } + + [Theory] + [InlineData(true)] + [InlineData(false)] + public void Child_windows_must_not_close_before_parent_has_chance_to_Cancel_OSCloseButton(bool programaticClose) + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var window = new Window(); + var child = new Window(); + + int count = 0; + int windowClosing = 0; + int childClosing = 0; + int windowClosed = 0; + int childClosed = 0; + + window.Closing += (sender, e) => + { + count++; + windowClosing = count; + e.Cancel = true; + }; + + child.Closing += (sender, e) => + { + count++; + childClosing = count; + }; + + window.Closed += (sender, e) => + { + count++; + windowClosed = count; + }; + + child.Closed += (sender, e) => + { + count++; + childClosed = count; + }; + + window.Show(); + child.Show(window); + + if (programaticClose) + { + window.Close(); + } + else + { + var cancel = window.PlatformImpl.Closing(); + + Assert.Equal(true, cancel); + } + + Assert.Equal(2, windowClosing); + Assert.Equal(1, childClosing); + Assert.Equal(0, windowClosed); + Assert.Equal(0, childClosed); + } + } [Fact] public void Showing_Should_Start_Renderer()