diff --git a/src/Avalonia.Controls/Button.cs b/src/Avalonia.Controls/Button.cs
index 6dba33516b..8e5d4e1e06 100644
--- a/src/Avalonia.Controls/Button.cs
+++ b/src/Avalonia.Controls/Button.cs
@@ -1,4 +1,5 @@
using System;
+using System.Diagnostics;
using System.Linq;
using System.Windows.Input;
using Avalonia.Automation.Peers;
@@ -281,24 +282,29 @@ namespace Avalonia.Controls
///
protected override void OnKeyDown(KeyEventArgs e)
{
- if (e.Key == Key.Enter)
+ switch (e.Key)
{
- OnClick();
- e.Handled = true;
- }
- else if (e.Key == Key.Space)
- {
- if (ClickMode == ClickMode.Press)
- {
+ case Key.Enter:
OnClick();
+ e.Handled = true;
+ break;
+
+ case Key.Space:
+ {
+ if (ClickMode == ClickMode.Press)
+ {
+ OnClick();
+ }
+
+ IsPressed = true;
+ e.Handled = true;
+ break;
}
- IsPressed = true;
- e.Handled = true;
- }
- else if (e.Key == Key.Escape && Flyout != null)
- {
- // If Flyout doesn't have focusable content, close the flyout here
- Flyout.Hide();
+
+ case Key.Escape when Flyout != null:
+ // If Flyout doesn't have focusable content, close the flyout here
+ CloseFlyout();
+ break;
}
base.OnKeyDown(e);
@@ -327,7 +333,14 @@ namespace Avalonia.Controls
{
if (IsEffectivelyEnabled)
{
- OpenFlyout();
+ if (_isFlyoutOpen)
+ {
+ CloseFlyout();
+ }
+ else
+ {
+ OpenFlyout();
+ }
var e = new RoutedEventArgs(ClickEvent);
RaiseEvent(e);
@@ -348,6 +361,14 @@ namespace Avalonia.Controls
Flyout?.ShowAt(this);
}
+ ///
+ /// Closes the button's flyout.
+ ///
+ protected virtual void CloseFlyout()
+ {
+ Flyout?.Hide();
+ }
+
///
/// Invoked when the button's flyout is opened.
///
@@ -494,8 +515,7 @@ namespace Avalonia.Controls
// If flyout is changed while one is already open, make sure we
// close the old one first
- if (oldFlyout != null &&
- oldFlyout.IsOpen)
+ if (oldFlyout != null && oldFlyout.IsOpen)
{
oldFlyout.Hide();
}
diff --git a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs
index 1504d2b25f..00ebcab70e 100644
--- a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs
+++ b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs
@@ -12,17 +12,12 @@ namespace Avalonia.Controls.Primitives
{
public abstract class FlyoutBase : AvaloniaObject, IPopupHostProvider
{
- static FlyoutBase()
- {
- Control.ContextFlyoutProperty.Changed.Subscribe(OnContextFlyoutPropertyChanged);
- }
-
///
/// Defines the property
///
public static readonly DirectProperty IsOpenProperty =
- AvaloniaProperty.RegisterDirect(nameof(IsOpen),
- x => x.IsOpen);
+ AvaloniaProperty.RegisterDirect(nameof(IsOpen),
+ x => x.IsOpen);
///
/// Defines the property
@@ -43,6 +38,14 @@ namespace Avalonia.Controls.Primitives
AvaloniaProperty.RegisterDirect(nameof(ShowMode),
x => x.ShowMode, (x, v) => x.ShowMode = v);
+ ///
+ /// Defines the property
+ ///
+ public static readonly DirectProperty OverlayInputPassThroughElementProperty =
+ Popup.OverlayInputPassThroughElementProperty.AddOwner(
+ o => o._overlayInputPassThroughElement,
+ (o, v) => o._overlayInputPassThroughElement = v);
+
///
/// Defines the AttachedFlyout property
///
@@ -57,6 +60,12 @@ namespace Avalonia.Controls.Primitives
private PixelRect? _enlargePopupRectScreenPixelRect;
private IDisposable? _transientDisposable;
private Action? _popupHostChangedHandler;
+ private IInputElement? _overlayInputPassThroughElement;
+
+ static FlyoutBase()
+ {
+ Control.ContextFlyoutProperty.Changed.Subscribe(OnContextFlyoutPropertyChanged);
+ }
public FlyoutBase()
{
@@ -101,11 +110,21 @@ namespace Avalonia.Controls.Primitives
private set => SetAndRaise(TargetProperty, ref _target, value);
}
+ ///
+ /// Gets or sets an element that should receive pointer input events even when underneath
+ /// the flyout's overlay.
+ ///
+ public IInputElement? OverlayInputPassThroughElement
+ {
+ get => _overlayInputPassThroughElement;
+ set => SetAndRaise(OverlayInputPassThroughElementProperty, ref _overlayInputPassThroughElement, value);
+ }
+
IPopupHost? IPopupHostProvider.PopupHost => Popup?.Host;
- event Action? IPopupHostProvider.PopupHostChanged
- {
- add => _popupHostChangedHandler += value;
+ event Action? IPopupHostProvider.PopupHostChanged
+ {
+ add => _popupHostChangedHandler += value;
remove => _popupHostChangedHandler -= value;
}
@@ -175,8 +194,9 @@ namespace Avalonia.Controls.Primitives
IsOpen = false;
Popup.IsOpen = false;
+
((ISetLogicalParent)Popup).SetParent(null);
-
+
// Ensure this isn't active
_transientDisposable?.Dispose();
_transientDisposable = null;
@@ -231,6 +251,8 @@ namespace Avalonia.Controls.Primitives
Popup.Child = CreatePresenter();
}
+ Popup.OverlayInputPassThroughElement = OverlayInputPassThroughElement;
+
if (CancelOpening())
{
return false;
@@ -356,10 +378,13 @@ namespace Avalonia.Controls.Primitives
private Popup CreatePopup()
{
- var popup = new Popup();
- popup.WindowManagerAddShadowHint = false;
- popup.IsLightDismissEnabled = true;
- popup.OverlayDismissEventPassThrough = true;
+ var popup = new Popup
+ {
+ WindowManagerAddShadowHint = false,
+ IsLightDismissEnabled = true,
+ //Note: This is required to prevent Button.Flyout from opening the flyout again after dismiss.
+ OverlayDismissEventPassThrough = false
+ };
popup.Opened += OnPopupOpened;
popup.Closed += OnPopupClosed;
@@ -372,7 +397,7 @@ namespace Avalonia.Controls.Primitives
{
IsOpen = true;
- _popupHostChangedHandler?.Invoke(Popup!.Host);
+ _popupHostChangedHandler?.Invoke(Popup.Host);
}
private void OnPopupClosing(object? sender, CancelEventArgs e)
diff --git a/src/Avalonia.Controls/Primitives/Popup.cs b/src/Avalonia.Controls/Primitives/Popup.cs
index 1501d97470..3573ad9aaa 100644
--- a/src/Avalonia.Controls/Primitives/Popup.cs
+++ b/src/Avalonia.Controls/Primitives/Popup.cs
@@ -501,7 +501,7 @@ namespace Avalonia.Controls.Primitives
if (dismissLayer != null)
{
dismissLayer.IsVisible = true;
- dismissLayer.InputPassThroughElement = _overlayInputPassThroughElement;
+ dismissLayer.InputPassThroughElement = OverlayInputPassThroughElement;
Disposable.Create(() =>
{