diff --git a/EleCho.WpfSuite.Helpers/Helpers/WindowOption.NativeMethods.cs b/EleCho.WpfSuite.Helpers/Helpers/WindowOption.NativeMethods.cs index 7287c75..3fcf9c9 100644 --- a/EleCho.WpfSuite.Helpers/Helpers/WindowOption.NativeMethods.cs +++ b/EleCho.WpfSuite.Helpers/Helpers/WindowOption.NativeMethods.cs @@ -160,6 +160,7 @@ public enum DwmWindowAttribute TEXT_COLOR, VISIBLE_FRAME_BORDER_THICKNESS, SYSTEMBACKDROP_TYPE, + SYSTEMBACKDROP_ALWAYS_ACTIVE, LAST } diff --git a/EleCho.WpfSuite.Helpers/Helpers/WindowOption.cs b/EleCho.WpfSuite.Helpers/Helpers/WindowOption.cs index dee0476..2341e44 100644 --- a/EleCho.WpfSuite.Helpers/Helpers/WindowOption.cs +++ b/EleCho.WpfSuite.Helpers/Helpers/WindowOption.cs @@ -89,6 +89,40 @@ public static void SetBackdrop(DependencyObject obj, WindowBackdrop value) } + /// + /// Get value of IsBackdropAlwaysActive property + /// + /// + /// + /// + /// This property controls whether the window backdrop (Mica/Acrylic) remains active when the window loses focus. + /// By default (false), Windows automatically deactivates the backdrop when the window is not focused. + /// When set to true, the backdrop will remain active even when the window loses focus. + /// This property is primarily designed for Window objects, but also supports Popup, ToolTip, ContextMenu, and MenuItem for consistency with other WindowOption properties. + /// + [AttachedPropertyBrowsableForType(typeof(Window))] + public static bool GetIsBackdropAlwaysActive(DependencyObject obj) + { + return (bool)obj.GetValue(IsBackdropAlwaysActiveProperty); + } + + /// + /// Set value of IsBackdropAlwaysActive property + /// + /// + /// + /// + /// This property controls whether the window backdrop (Mica/Acrylic) remains active when the window loses focus. + /// By default (false), Windows automatically deactivates the backdrop when the window is not focused. + /// When set to true, the backdrop will remain active even when the window loses focus. + /// This property is primarily designed for Window objects, but also supports Popup, ToolTip, ContextMenu, and MenuItem for consistency with other WindowOption properties. + /// + public static void SetIsBackdropAlwaysActive(DependencyObject obj, bool value) + { + obj.SetValue(IsBackdropAlwaysActiveProperty, value); + } + + /// /// Get value of Corner property /// @@ -398,6 +432,18 @@ public static void SetIsCaption(DependencyObject obj, bool value) public static readonly DependencyProperty BackdropProperty = DependencyProperty.RegisterAttached("Backdrop", typeof(WindowBackdrop), typeof(WindowOption), new FrameworkPropertyMetadata(WindowBackdrop.Auto, OnBackdropChanged)); + /// + /// The DependencyProperty of IsBackdropAlwaysActive property + /// + /// + /// Controls whether the window backdrop (Mica/Acrylic) remains active when the window loses focus. + /// Default value is false, meaning the backdrop will be deactivated when the window loses focus (standard Windows behavior). + /// When set to true, the backdrop will remain active even when the window is not focused. + /// This property requires Windows 11 Build 22621 or later. + /// + public static readonly DependencyProperty IsBackdropAlwaysActiveProperty = + DependencyProperty.RegisterAttached("IsBackdropAlwaysActive", typeof(bool), typeof(WindowOption), new FrameworkPropertyMetadata(false, OnIsBackdropAlwaysActiveChanged)); + /// /// The DependencyProperty of Corner property /// @@ -1010,6 +1056,54 @@ backdrop is not WindowBackdrop.Auto && } } + private static void OnIsBackdropAlwaysActiveChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var isAlwaysActive = GetIsBackdropAlwaysActive(d); + + if (GetWindowHwndSource(d) is HwndSource hwndSource) + { + ApplyIsBackdropAlwaysActive(hwndSource, isAlwaysActive); + } + + // Always remove the event handler first to prevent duplicates + if (d is Window window) + { + window.SourceInitialized -= EventHandlerApplyIsBackdropAlwaysActive; + if (isAlwaysActive) + window.SourceInitialized += EventHandlerApplyIsBackdropAlwaysActive; + } + else if (d is Popup popup) + { + popup.Opened -= EventHandlerApplyIsBackdropAlwaysActive; + if (isAlwaysActive) + popup.Opened += EventHandlerApplyIsBackdropAlwaysActive; + } + else if (d is ToolTip toolTip) + { + toolTip.Opened -= EventHandlerApplyIsBackdropAlwaysActive; + if (isAlwaysActive) + toolTip.Opened += EventHandlerApplyIsBackdropAlwaysActive; + } + else if (d is ContextMenu contextMenu) + { + contextMenu.Opened -= EventHandlerApplyIsBackdropAlwaysActive; + if (isAlwaysActive) + contextMenu.Opened += EventHandlerApplyIsBackdropAlwaysActive; + } + else if (d is MenuItem menuItem) + { + menuItem.SubmenuOpened -= EventHandlerApplyIsBackdropAlwaysActive; + if (isAlwaysActive) + menuItem.SubmenuOpened += EventHandlerApplyIsBackdropAlwaysActive; + } + else if (d is FrameworkElement element) + { + element.Loaded -= EventHandlerApplyIsBackdropAlwaysActive; + if (isAlwaysActive) + element.Loaded += EventHandlerApplyIsBackdropAlwaysActive; + } + } + private static void OnCornerChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var corner = GetCorner(d); @@ -1988,6 +2082,17 @@ private static unsafe void ApplyCorner(HwndSource hwndSource, WindowCorner corne DwmSetWindowAttribute(handle, DwmWindowAttribute.WINDOW_CORNER_PREFERENCE, (nint)(void*)&corner, (uint)sizeof(WindowCorner)); } + private static unsafe void ApplyIsBackdropAlwaysActive(HwndSource hwndSource, bool isAlwaysActive) + { + // this api is only available on windows 11 22621 + if (s_versionCurrentWindows < s_versionWindows11_22621) + return; + + var handle = hwndSource.Handle; + + DwmSetWindowAttribute(handle, DwmWindowAttribute.SYSTEMBACKDROP_ALWAYS_ACTIVE, (nint)(void*)&isAlwaysActive, (uint)sizeof(bool)); + } + private static unsafe void ApplyCaptionColor(HwndSource hwndSource, WindowOptionColor color) { // this api is only available on windows 11 22000 @@ -2423,6 +2528,13 @@ private static void EventHandlerApplyCorner(object? sender, EventArgs e) ApplyCorner(hwndSource, GetCorner(d)); } + private static void EventHandlerApplyIsBackdropAlwaysActive(object? sender, EventArgs e) + { + if (sender is DependencyObject d && + GetWindowHwndSource(d) is HwndSource hwndSource) + ApplyIsBackdropAlwaysActive(hwndSource, GetIsBackdropAlwaysActive(d)); + } + private static void EventHandlerApplyCaptionColor(object? sender, EventArgs e) { if (sender is DependencyObject d && diff --git a/WpfTest/Tests/BackdropAlwaysActiveTestWindow.xaml b/WpfTest/Tests/BackdropAlwaysActiveTestWindow.xaml new file mode 100644 index 0000000..8153edc --- /dev/null +++ b/WpfTest/Tests/BackdropAlwaysActiveTestWindow.xaml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +