Browse Source

Fix popups not closing when using WinForms Host (#20090)

* fix popups not closing in embedded view in winforms

* fix winforms integration test sample

* addressed review
Emmanuel Hansen 1 week ago
parent
commit
8631917d57

+ 18 - 0
src/Avalonia.Controls/Primitives/Popup.cs

@@ -536,6 +536,16 @@ namespace Avalonia.Controls.Primitives
                         (x, handler) => x.Closed -= handler).DisposeWith(handlerCleanup);
                 }
             }
+            else if (topLevel is { } wtl && wtl.PlatformImpl is IWindowBaseImpl wimpl)
+            {
+                SubscribeToEventHandler<ITopLevelImpl, Action>(wimpl, TopLevelLostPlatformFocus,
+                    (x, handler) => x.LostFocus += handler,
+                    (x, handler) => x.LostFocus -= handler).DisposeWith(handlerCleanup);
+
+                SubscribeToEventHandler<IWindowBaseImpl, Action>(wimpl, WindowBaseDeactivated,
+                    (x, handler) => x.Deactivated += handler,
+                    (x, handler) => x.Deactivated -= handler).DisposeWith(handlerCleanup);
+            }
             else if (topLevel is { } tl && tl.PlatformImpl is ITopLevelImpl pimpl)
             {
                 SubscribeToEventHandler<ITopLevelImpl, Action>(pimpl, TopLevelLostPlatformFocus,
@@ -987,6 +997,14 @@ namespace Avalonia.Controls.Primitives
             }
         }
 
+        private void WindowBaseDeactivated()
+        {
+            if (IsLightDismissEnabled)
+            {
+                Close();
+            }
+        }
+
         private void ParentClosed(object? sender, EventArgs e)
         {
             if (IsLightDismissEnabled)

+ 26 - 1
src/Windows/Avalonia.Win32.Interoperability/WinForms/WinFormsAvaloniaControlHost.cs

@@ -14,7 +14,7 @@ namespace Avalonia.Win32.Interoperability;
 /// An element that allows you to host a Avalonia control on a Windows Forms page.
 /// </summary>
 [ToolboxItem(true)]
-public class WinFormsAvaloniaControlHost : WinFormsControl
+public class WinFormsAvaloniaControlHost : WinFormsControl, IMessageFilter
 {
     private AvControl? _content;
     private EmbeddableControlRoot? _root;
@@ -66,11 +66,14 @@ public class WinFormsAvaloniaControlHost : WinFormsControl
         }
 
         base.OnHandleCreated(e);
+
+        System.Windows.Forms.Application.AddMessageFilter(this);
     }
 
     /// <inheritdoc />
     protected override void OnHandleDestroyed(EventArgs e)
     {
+        System.Windows.Forms.Application.RemoveMessageFilter(this);
         _root?.StopRendering();
         _root?.Dispose();
         _root = null;
@@ -135,4 +138,26 @@ public class WinFormsAvaloniaControlHost : WinFormsControl
             e.Graphics.DrawString(message, Font, SystemBrushes.ControlText, messageArea);
         }
     }
+
+    public bool PreFilterMessage(ref Message m)
+    {
+        var message = (UnmanagedMethods.WindowsMessage)m.Msg;
+
+        switch (message)
+        {
+            case UnmanagedMethods.WindowsMessage.WM_LBUTTONDOWN:
+            case UnmanagedMethods.WindowsMessage.WM_MBUTTONDOWN:
+            case UnmanagedMethods.WindowsMessage.WM_RBUTTONDOWN:
+            case UnmanagedMethods.WindowsMessage.WM_NCLBUTTONDOWN:
+            case UnmanagedMethods.WindowsMessage.WM_NCMBUTTONDOWN:
+            case UnmanagedMethods.WindowsMessage.WM_NCRBUTTONDOWN:
+                if (_root?.PlatformImpl is WindowImpl impl && !impl.IsOurWindow(m.HWnd))
+                {
+                    impl.Deactivated?.Invoke();
+                }
+                break;
+        }
+
+        return false;
+    }
 }

+ 1 - 1
src/Windows/Avalonia.Win32/WindowImpl.AppWndProc.cs

@@ -900,7 +900,7 @@ namespace Avalonia.Win32
             return DefWindowProc(hWnd, msg, wParam, lParam);
         }
 
-        private bool IsOurWindow(IntPtr hwnd)
+        internal bool IsOurWindow(IntPtr hwnd)
         {
             if (hwnd == IntPtr.Zero)
                 return false;