diff --git a/src/Wpf.Ui/Controls/FluentWindow/FluentWindow.cs b/src/Wpf.Ui/Controls/FluentWindow/FluentWindow.cs index bccafdb30..d019c38a9 100644 --- a/src/Wpf.Ui/Controls/FluentWindow/FluentWindow.cs +++ b/src/Wpf.Ui/Controls/FluentWindow/FluentWindow.cs @@ -4,7 +4,9 @@ // All Rights Reserved. using System.Windows.Shell; +using Wpf.Ui.Appearance; using Wpf.Ui.Interop; +using Wpf.Ui.Win32; // ReSharper disable once CheckNamespace namespace Wpf.Ui.Controls; @@ -82,6 +84,17 @@ public bool ExtendsContentIntoTitleBar public FluentWindow() { SetResourceReference(StyleProperty, typeof(FluentWindow)); + + if (Utilities.IsOSWindows11OrNewer) + { + ApplicationThemeManager.Changed += (_, _) => + { + if (IsActive) + { + UnsafeNativeMethods.ApplyBorderColor(this, ApplicationAccentColorManager.SystemAccent); + } + }; + } } /// @@ -103,12 +116,35 @@ static FluentWindow() protected override void OnSourceInitialized(EventArgs e) { OnCornerPreferenceChanged(default, WindowCornerPreference); - OnExtendsContentIntoTitleBarChanged(default, ExtendsContentIntoTitleBar); + OnExtendsContentIntoTitleBarChanged(false, ExtendsContentIntoTitleBar); OnBackdropTypeChanged(default, WindowBackdropType); base.OnSourceInitialized(e); } + /// + protected override void OnActivated(EventArgs e) + { + base.OnActivated(e); + + if (Utilities.IsOSWindows11OrNewer) + { + UnsafeNativeMethods.ApplyBorderColor(this, ApplicationAccentColorManager.SystemAccent); + } + } + + /// + protected override void OnDeactivated(EventArgs e) + { + base.OnDeactivated(e); + + if (Utilities.IsOSWindows11OrNewer) + { + // DWMWA_COLOR_DEFAULT. + UnsafeNativeMethods.ApplyBorderColor(this, unchecked((int)0xFFFFFFFF)); + } + } + /// /// Private property callback. /// @@ -182,10 +218,11 @@ protected virtual void OnBackdropTypeChanged(WindowBackdropType oldValue, Window return; } + SetWindowChrome(); + if (newValue == WindowBackdropType.None) { _ = WindowBackdrop.RemoveBackdrop(this); - return; } @@ -233,20 +270,36 @@ protected virtual void OnExtendsContentIntoTitleBarChanged(bool oldValue, bool n // AllowsTransparency = true; SetCurrentValue(WindowStyleProperty, WindowStyle.SingleBorderWindow); - WindowChrome.SetWindowChrome( - this, - new WindowChrome - { - CaptionHeight = 0, - CornerRadius = default, - GlassFrameThickness = new Thickness(-1), - ResizeBorderThickness = ResizeMode == ResizeMode.NoResize ? default : new Thickness(4), - UseAeroCaptionButtons = false, - } - ); - // WindowStyleProperty.OverrideMetadata(typeof(FluentWindow), new FrameworkPropertyMetadata(WindowStyle.SingleBorderWindow)); // AllowsTransparencyProperty.OverrideMetadata(typeof(FluentWindow), new FrameworkPropertyMetadata(false)); _ = UnsafeNativeMethods.RemoveWindowTitlebarContents(this); } + + /// + /// This virtual method is called when is changed. + /// + protected virtual void SetWindowChrome() + { + try + { + if (Utilities.IsCompositionEnabled) + { + WindowChrome.SetWindowChrome( + this, + new WindowChrome + { + CaptionHeight = 0, + CornerRadius = default, + GlassFrameThickness = WindowBackdropType == WindowBackdropType.None ? new Thickness(0.00001) : new Thickness(-1), // 0.00001 so there's no glass frame drawn around the window, but the border is still drawn. + ResizeBorderThickness = ResizeMode == ResizeMode.NoResize ? default : new Thickness(4), + UseAeroCaptionButtons = false, + } + ); + } + } + catch (COMException) + { + // Ignored. + } + } } diff --git a/src/Wpf.Ui/Interop/UnsafeNativeMethods.cs b/src/Wpf.Ui/Interop/UnsafeNativeMethods.cs index be8c6f217..d12877871 100644 --- a/src/Wpf.Ui/Interop/UnsafeNativeMethods.cs +++ b/src/Wpf.Ui/Interop/UnsafeNativeMethods.cs @@ -49,15 +49,64 @@ public static bool ApplyWindowCornerPreference(IntPtr handle, WindowCornerPrefer int pvAttribute = (int)UnsafeReflection.Cast(cornerPreference); - // TODO: Validate HRESULT - _ = Dwmapi.DwmSetWindowAttribute( + return Dwmapi.DwmSetWindowAttribute( handle, Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_WINDOW_CORNER_PREFERENCE, ref pvAttribute, Marshal.SizeOf(typeof(int)) - ); + ) == 0; + } - return true; + /// + /// Tries to apply the color of the border. + /// + /// The window. + /// The color. + /// if invocation of native Windows function succeeds. + public static bool ApplyBorderColor(Window window, Color color) => + GetHandle(window, out IntPtr windowHandle) + && ApplyBorderColor(windowHandle, color); + + /// + /// Tries to apply the color of the border. + /// + /// The window. + /// The color. + /// if invocation of native Windows function succeeds. + public static bool ApplyBorderColor(Window window, int color) => + GetHandle(window, out IntPtr windowHandle) + && ApplyBorderColor(windowHandle, color); + + /// + /// Tries to apply the color of the border. + /// + /// The handle. + /// The color. + /// if invocation of native Windows function succeeds. + public static bool ApplyBorderColor(IntPtr handle, Color color) + { + return ApplyBorderColor(handle, (color.B << 16) | (color.G << 8) | color.R); + } + + /// + /// Tries to apply the color of the border. + /// + /// The handle. + /// The color. + /// if invocation of native Windows function succeeds. + public static bool ApplyBorderColor(IntPtr handle, int color) + { + if (handle == IntPtr.Zero) + { + return false; + } + + if (!User32.IsWindow(handle)) + { + return false; + } + + return Dwmapi.DwmSetWindowAttribute(handle, Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_BORDER_COLOR, ref color, sizeof(int)) == 0; } /// @@ -93,10 +142,7 @@ public static bool RemoveWindowDarkMode(IntPtr handle) dwAttribute = Dwmapi.DWMWINDOWATTRIBUTE.DMWA_USE_IMMERSIVE_DARK_MODE_OLD; } - // TODO: Validate HRESULT - _ = Dwmapi.DwmSetWindowAttribute(handle, dwAttribute, ref pvAttribute, Marshal.SizeOf(typeof(int))); - - return true; + return Dwmapi.DwmSetWindowAttribute(handle, dwAttribute, ref pvAttribute, Marshal.SizeOf(typeof(int))) == 0; } /// @@ -132,10 +178,7 @@ public static bool ApplyWindowDarkMode(IntPtr handle) dwAttribute = Dwmapi.DWMWINDOWATTRIBUTE.DMWA_USE_IMMERSIVE_DARK_MODE_OLD; } - // TODO: Validate HRESULT - _ = Dwmapi.DwmSetWindowAttribute(handle, dwAttribute, ref pvAttribute, Marshal.SizeOf(typeof(int))); - - return true; + return Dwmapi.DwmSetWindowAttribute(handle, dwAttribute, ref pvAttribute, Marshal.SizeOf(typeof(int))) == 0; } /// @@ -214,15 +257,12 @@ public static bool ApplyWindowBackdrop(IntPtr handle, WindowBackdropType backgro return false; } - // TODO: Validate HRESULT - _ = Dwmapi.DwmSetWindowAttribute( + return Dwmapi.DwmSetWindowAttribute( handle, Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_SYSTEMBACKDROP_TYPE, ref backdropPvAttribute, Marshal.SizeOf(typeof(int)) - ); - - return true; + ) == 0; } /// @@ -296,15 +336,12 @@ public static bool ApplyWindowLegacyMicaEffect(IntPtr handle) { var backdropPvAttribute = 0x1; // Enable - // TODO: Validate HRESULT - _ = Dwmapi.DwmSetWindowAttribute( + return Dwmapi.DwmSetWindowAttribute( handle, Dwmapi.DWMWINDOWATTRIBUTE.DWMWA_MICA_EFFECT, ref backdropPvAttribute, Marshal.SizeOf(typeof(int)) - ); - - return true; + ) == 0; } ///