Browse Source

Merge pull request #1383 from CommonGuy/closing-event

Closing event #1252
Steven Kirk 7 years ago
parent
commit
3077d8bf18

+ 6 - 0
src/Avalonia.Controls/Platform/IWindowImpl.cs

@@ -44,5 +44,11 @@ namespace Avalonia.Platform
         /// Enables or disables the taskbar icon
         /// </summary>
         void ShowTaskbarIcon(bool value);
+
+        /// <summary>
+        /// Gets or sets a method called before the underlying implementation is destroyed.
+        /// Return true to prevent the underlying implementation from closing.
+        /// </summary>
+        Func<bool> Closing { get; set; }
     }
 }

+ 38 - 5
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
         /// <inheritdoc/>
         Type IStyleable.StyleKey => typeof(Window);
 
+        /// <summary>
+        /// Fired before a window is closed.
+        /// </summary>
+        public event EventHandler<CancelEventArgs> Closing;
+
         /// <summary>
         /// Closes the window.
         /// </summary>
         public void Close()
         {
-            s_windows.Remove(this);
-            PlatformImpl?.Dispose();
-            IsVisible = false;
+            Close(false);
         }
 
         protected override void HandleApplicationExiting()
         {
             base.HandleApplicationExiting();
-            Close();
+            Close(true);
         }
 
         /// <summary>
@@ -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;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Handles a closing notification from <see cref="IWindowImpl.Closing"/>.
+        /// </summary>
+        protected virtual bool HandleClosing()
+        {
+            var args = new CancelEventArgs();
+            Closing?.Invoke(this, args);
+            return args.Cancel;
         }
 
         /// <summary>

+ 1 - 0
src/Avalonia.DesignerSupport/Remote/PreviewerWindowImpl.cs

@@ -39,6 +39,7 @@ namespace Avalonia.DesignerSupport.Remote
         public Action<Point> PositionChanged { get; set; }
         public Action Deactivated { get; set; }
         public Action Activated { get; set; }
+        public Func<bool> Closing { get; set; }
         public IPlatformHandle Handle { get; }
         public WindowState WindowState { get; set; }
         public Size MaxClientSize { get; } = new Size(4096, 4096);

+ 1 - 0
src/Avalonia.DesignerSupport/Remote/Stubs.cs

@@ -26,6 +26,7 @@ namespace Avalonia.DesignerSupport.Remote
         public Action<Rect> Paint { get; set; }
         public Action<Size> Resized { get; set; }
         public Action<double> ScalingChanged { get; set; }
+        public Func<bool> Closing { get; set; }
         public Action Closed { get; set; }
         public IMouseDevice MouseDevice { get; } = new MouseDevice();
         public Point Position { get; set; }

+ 8 - 0
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<Native.D.signal_generic>("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<bool> Closing { get; set; }
         public Action Closed { get; set; }
         public Action Deactivated { get; set; }
         public Action<RawInputEventArgs> Input { get; set; }

+ 8 - 0
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<Point> PositionChanged { get; set; }
         public Action Deactivated { get; set; }
         public Action Activated { get; set; }
+        public Func<bool> Closing { get; set; }
 
         public override Size ClientSize => Window.ContentRectFor(Window.Frame).Size.ToAvaloniaSize();
 

+ 10 - 0
src/Windows/Avalonia.Win32/WindowImpl.cs

@@ -56,6 +56,8 @@ namespace Avalonia.Win32
 
         public Action Activated { get; set; }
 
+        public Func<bool> 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;