diff --git a/src/Avalonia.Controls/Platform/IWindowImpl.cs b/src/Avalonia.Controls/Platform/IWindowImpl.cs
index 37637b1624..1f84574318 100644
--- a/src/Avalonia.Controls/Platform/IWindowImpl.cs
+++ b/src/Avalonia.Controls/Platform/IWindowImpl.cs
@@ -44,5 +44,11 @@ namespace Avalonia.Platform
/// Enables or disables the taskbar icon
///
void ShowTaskbarIcon(bool value);
+
+ ///
+ /// Gets or sets a method called before the underlying implementation is destroyed.
+ /// Return true to prevent the underlying implementation from closing.
+ ///
+ Func Closing { get; set; }
}
}
diff --git a/src/Avalonia.Controls/Window.cs b/src/Avalonia.Controls/Window.cs
index 9db7db365d..c66209d3c6 100644
--- a/src/Avalonia.Controls/Window.cs
+++ b/src/Avalonia.Controls/Window.cs
@@ -13,6 +13,7 @@ using Avalonia.Styling;
using System.Collections.Generic;
using System.Linq;
using JetBrains.Annotations;
+using System.ComponentModel;
namespace Avalonia.Controls
{
@@ -129,6 +130,7 @@ namespace Avalonia.Controls
public Window(IWindowImpl impl)
: base(impl)
{
+ impl.Closing = HandleClosing;
_maxPlatformClientSize = PlatformImpl?.MaxClientSize ?? default(Size);
Screens = new Screens(PlatformImpl?.Screen);
}
@@ -230,20 +232,23 @@ namespace Avalonia.Controls
///
Type IStyleable.StyleKey => typeof(Window);
+ ///
+ /// Fired before a window is closed.
+ ///
+ public event EventHandler Closing;
+
///
/// Closes the window.
///
public void Close()
{
- s_windows.Remove(this);
- PlatformImpl?.Dispose();
- IsVisible = false;
+ Close(false);
}
protected override void HandleApplicationExiting()
{
base.HandleApplicationExiting();
- Close();
+ Close(true);
}
///
@@ -258,7 +263,35 @@ namespace Avalonia.Controls
public void Close(object dialogResult)
{
_dialogResult = dialogResult;
- Close();
+ Close(false);
+ }
+
+ internal void Close(bool ignoreCancel)
+ {
+ var cancelClosing = false;
+ try
+ {
+ cancelClosing = HandleClosing();
+ }
+ finally
+ {
+ if (ignoreCancel || !cancelClosing)
+ {
+ s_windows.Remove(this);
+ PlatformImpl?.Dispose();
+ IsVisible = false;
+ }
+ }
+ }
+
+ ///
+ /// Handles a closing notification from .
+ ///
+ protected virtual bool HandleClosing()
+ {
+ var args = new CancelEventArgs();
+ Closing?.Invoke(this, args);
+ return args.Cancel;
}
///
diff --git a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
index 3c7ef86d5d..fc9541abb7 100644
--- a/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
+++ b/src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs
@@ -39,6 +39,7 @@ namespace Avalonia.DesignerSupport.Remote
public Action PositionChanged { get; set; }
public Action Deactivated { get; set; }
public Action Activated { get; set; }
+ public Func Closing { get; set; }
public IPlatformHandle Handle { get; }
public WindowState WindowState { get; set; }
public Size MaxClientSize { get; } = new Size(4096, 4096);
diff --git a/src/Avalonia.DesignerSupport/Remote/Stubs.cs b/src/Avalonia.DesignerSupport/Remote/Stubs.cs
index 2ed434a2dc..560425286e 100644
--- a/src/Avalonia.DesignerSupport/Remote/Stubs.cs
+++ b/src/Avalonia.DesignerSupport/Remote/Stubs.cs
@@ -26,6 +26,7 @@ namespace Avalonia.DesignerSupport.Remote
public Action Paint { get; set; }
public Action Resized { get; set; }
public Action ScalingChanged { get; set; }
+ public Func Closing { get; set; }
public Action Closed { get; set; }
public IMouseDevice MouseDevice { get; } = new MouseDevice();
public Point Position { get; set; }
diff --git a/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs b/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
index 5aac4466b2..a42c8a19b9 100644
--- a/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
+++ b/src/Gtk/Avalonia.Gtk3/WindowBaseImpl.cs
@@ -54,6 +54,7 @@ namespace Avalonia.Gtk3
ConnectEvent("key-press-event", OnKeyEvent);
ConnectEvent("key-release-event", OnKeyEvent);
ConnectEvent("leave-notify-event", OnLeaveNotifyEvent);
+ ConnectEvent("delete-event", OnClosingEvent);
Connect("destroy", OnDestroy);
Native.GtkWidgetRealize(gtkWidget);
GdkWindowHandle = this.Handle.Handle;
@@ -125,6 +126,12 @@ namespace Avalonia.Gtk3
return rv;
}
+ private unsafe bool OnClosingEvent(IntPtr w, IntPtr ev, IntPtr userdata)
+ {
+ bool? preventClosing = Closing?.Invoke();
+ return preventClosing ?? false;
+ }
+
private unsafe bool OnButton(IntPtr w, IntPtr ev, IntPtr userdata)
{
var evnt = (GdkEventButton*)ev;
@@ -343,6 +350,7 @@ namespace Avalonia.Gtk3
string IPlatformHandle.HandleDescriptor => "HWND";
public Action Activated { get; set; }
+ public Func Closing { get; set; }
public Action Closed { get; set; }
public Action Deactivated { get; set; }
public Action Input { get; set; }
diff --git a/src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs b/src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs
index 9ce1756aae..e5ba285f4f 100644
--- a/src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs
+++ b/src/OSX/Avalonia.MonoMac/WindowBaseImpl.cs
@@ -4,6 +4,7 @@ using Avalonia.Input.Raw;
using Avalonia.Platform;
using MonoMac.AppKit;
using MonoMac.CoreGraphics;
+using MonoMac.Foundation;
using MonoMac.ObjCRuntime;
namespace Avalonia.MonoMac
@@ -69,6 +70,12 @@ namespace Avalonia.MonoMac
_impl.PositionChanged?.Invoke(_impl.Position);
}
+ public override bool WindowShouldClose(NSObject sender)
+ {
+ bool? preventClose = _impl.Closing?.Invoke();
+ return preventClose != true;
+ }
+
public override void WillClose(global::MonoMac.Foundation.NSNotification notification)
{
_impl.Window.Dispose();
@@ -107,6 +114,7 @@ namespace Avalonia.MonoMac
public Action PositionChanged { get; set; }
public Action Deactivated { get; set; }
public Action Activated { get; set; }
+ public Func Closing { get; set; }
public override Size ClientSize => Window.ContentRectFor(Window.Frame).Size.ToAvaloniaSize();
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs
index 3ba926b42a..a67362d59f 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.cs
@@ -56,6 +56,8 @@ namespace Avalonia.Win32
public Action Activated { get; set; }
+ public Func Closing { get; set; }
+
public Action Closed { get; set; }
public Action Deactivated { get; set; }
@@ -431,6 +433,14 @@ namespace Avalonia.Win32
return IntPtr.Zero;
+ case UnmanagedMethods.WindowsMessage.WM_CLOSE:
+ bool? preventClosing = Closing?.Invoke();
+ if (preventClosing == true)
+ {
+ return IntPtr.Zero;
+ }
+ break;
+
case UnmanagedMethods.WindowsMessage.WM_DESTROY:
//Window doesn't exist anymore
_hwnd = IntPtr.Zero;