Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 46 additions & 14 deletions src/Wpf.Ui/Controls/FluentWindow/FluentWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -103,12 +105,35 @@ static FluentWindow()
protected override void OnSourceInitialized(EventArgs e)
{
OnCornerPreferenceChanged(default, WindowCornerPreference);
OnExtendsContentIntoTitleBarChanged(default, ExtendsContentIntoTitleBar);
OnExtendsContentIntoTitleBarChanged(false, ExtendsContentIntoTitleBar);
OnBackdropTypeChanged(default, WindowBackdropType);

base.OnSourceInitialized(e);
}

/// <inheritdoc />
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);

if (Utilities.IsOSWindows11OrNewer)
{
UnsafeNativeMethods.ApplyBorderColor(this, ApplicationAccentColorManager.SystemAccent);
}
}

/// <inheritdoc />
protected override void OnDeactivated(EventArgs e)
{
base.OnDeactivated(e);

if (Utilities.IsOSWindows11OrNewer)
{
// DWMWA_COLOR_DEFAULT.
UnsafeNativeMethods.ApplyBorderColor(this, unchecked((int)0xFFFFFFFF));
}
}

/// <summary>
/// Private <see cref="WindowCornerPreference"/> property callback.
/// </summary>
Expand Down Expand Up @@ -182,10 +207,11 @@ protected virtual void OnBackdropTypeChanged(WindowBackdropType oldValue, Window
return;
}

SetWindowChrome();

if (newValue == WindowBackdropType.None)
{
_ = WindowBackdrop.RemoveBackdrop(this);

return;
}

Expand Down Expand Up @@ -233,20 +259,26 @@ 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);
}

/// <summary>
/// This virtual method is called when <see cref="WindowBackdropType"/> is changed.
/// </summary>
protected virtual void SetWindowChrome()
{
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.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the fix for FluentWindow. -1 cannot be used if None is specified, otherwise the background of the window is rendered 1 pixel inside, which is obviously wrong.

ResizeBorderThickness = ResizeMode == ResizeMode.NoResize ? default : new Thickness(4),
UseAeroCaptionButtons = false,
}
);
}
}
81 changes: 59 additions & 22 deletions src/Wpf.Ui/Interop/UnsafeNativeMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
/// <summary>
/// Tries to apply the color of the border.
/// </summary>
/// <param name="window">The window.</param>
/// <param name="color">The color.</param>
/// <returns><see langword="true" /> if invocation of native Windows function succeeds.</returns>
public static bool ApplyBorderColor(Window window, Color color) =>
GetHandle(window, out IntPtr windowHandle)
&& ApplyBorderColor(windowHandle, color);

/// <summary>
/// Tries to apply the color of the border.
/// </summary>
/// <param name="window">The window.</param>
/// <param name="color">The color.</param>
/// <returns><see langword="true" /> if invocation of native Windows function succeeds.</returns>
public static bool ApplyBorderColor(Window window, int color) =>
GetHandle(window, out IntPtr windowHandle)
&& ApplyBorderColor(windowHandle, color);

/// <summary>
/// Tries to apply the color of the border.
/// </summary>
/// <param name="handle">The handle.</param>
/// <param name="color">The color.</param>
/// <returns><see langword="true"/> if invocation of native Windows function succeeds.</returns>
public static bool ApplyBorderColor(IntPtr handle, Color color)
{
return ApplyBorderColor(handle, (color.B << 16) | (color.G << 8) | color.R);
}

/// <summary>
/// Tries to apply the color of the border.
/// </summary>
/// <param name="handle">The handle.</param>
/// <param name="color">The color.</param>
/// <returns><see langword="true"/> if invocation of native Windows function succeeds.</returns>
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;
}

/// <summary>
Expand Down Expand Up @@ -93,10 +142,7 @@ public static bool RemoveWindowDarkMode(IntPtr handle)
dwAttribute = Dwmapi.DWMWINDOWATTRIBUTE.DMWA_USE_IMMERSIVE_DARK_MODE_OLD;
}

// TODO: Validate HRESULT
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also corrected this, it's only returning 0 on success.

_ = Dwmapi.DwmSetWindowAttribute(handle, dwAttribute, ref pvAttribute, Marshal.SizeOf(typeof(int)));

return true;
return Dwmapi.DwmSetWindowAttribute(handle, dwAttribute, ref pvAttribute, Marshal.SizeOf(typeof(int))) == 0;
}

/// <summary>
Expand Down Expand Up @@ -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;
}

/// <summary>
Expand Down Expand Up @@ -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;
}

/// <summary>
Expand Down Expand Up @@ -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;
}

/// <summary>
Expand Down