using System.Windows.Shell;
using WPFluent.Appearance;
using WPFluent.Hardware;
using Size = System.Windows.Size;
// ReSharper disable once CheckNamespace
namespace WPFluent.Controls;
///
/// If you use to extend the UI elements to the non-client area, you can include this
/// container in the template of so that the content inside automatically fills the client area.
/// Using this container can let you get rid of various margin adaptations done in Setter/Trigger of the style of when the window state changes.
///
///
/// <Style x:Key="MyWindowCustomStyle" BasedOn="{StaticResource {x:Type Window}}"
/// TargetType="{x:Type controls:FluentWindow}"> <Setter Property="Template" > <Setter.Value>
/// <ControlTemplate TargetType="{x:Type Window}"> <AdornerDecorator> <controls:ClientAreaBorder
/// Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}"
/// BorderThickness="{TemplateBinding BorderThickness}"> <ContentPresenter x:Name="ContentPresenter" />
/// </controls:ClientAreaBorder> </AdornerDecorator> </ControlTemplate> </Setter.Value>
/// </Setter> </Style>
///
public class ClientAreaBorder : System.Windows.Controls.Border, IThemeControl
{
/*private const int SM_CXFRAME = 32;
private const int SM_CYFRAME = 33;
private const int SM_CXPADDEDBORDER = 92;*/
private static Thickness? _paddedBorderThickness;
private static Thickness? _resizeFrameBorderThickness;
private static Thickness? _windowChromeNonClientFrameThickness;
private bool _borderBrushApplied = false;
private System.Windows.Window? _oldWindow;
public ClientAreaBorder()
{
ApplicationTheme = Appearance.ApplicationThemeManager.GetAppTheme();
Appearance.ApplicationThemeManager.Changed += OnThemeChanged;
}
private void ApplyDefaultWindowBorder()
{
if (Win32.Utilities.IsOSWindows11OrNewer || _oldWindow == null)
{
return;
}
_borderBrushApplied = true;
// SystemParameters.WindowGlassBrush
Color borderColor =
ApplicationTheme == ApplicationTheme.Light
? Color.FromArgb(0xFF, 0x7A, 0x7A, 0x7A)
: Color.FromArgb(0xFF, 0x3A, 0x3A, 0x3A);
_oldWindow.SetCurrentValue(
System.Windows.Controls.Control.BorderBrushProperty,
new SolidColorBrush(borderColor));
_oldWindow.SetCurrentValue(System.Windows.Controls.Control.BorderThicknessProperty, new Thickness(1));
}
private (double FactorX, double FactorY) GetDpi()
{
if (PresentationSource.FromVisual(this) is { } source)
{
return (
source.CompositionTarget.TransformToDevice.M11, // Possible null reference
source.CompositionTarget.TransformToDevice.M22
);
}
DisplayDpi systemDPi = DpiHelper.GetSystemDpi();
return (systemDPi.DpiScaleX, systemDPi.DpiScaleY);
}
private void OnThemeChanged(ApplicationTheme currentApplicationTheme, Color systemAccent)
{
ApplicationTheme = currentApplicationTheme;
if (!_borderBrushApplied || _oldWindow == null)
{
return;
}
ApplyDefaultWindowBorder();
}
private void OnWindowClosing(object? sender, CancelEventArgs e)
{
Appearance.ApplicationThemeManager.Changed -= OnThemeChanged;
if (_oldWindow != null)
{
_oldWindow.Closing -= OnWindowClosing;
}
}
private void OnWindowStateChanged(object? sender, EventArgs e)
{
if (sender is not System.Windows.Window window)
{
return;
}
Thickness padding =
window.WindowState == WindowState.Maximized ? WindowChromeNonClientFrameThickness : default;
SetCurrentValue(PaddingProperty, padding);
}
///
protected override void OnVisualParentChanged(DependencyObject oldParent)
{
base.OnVisualParentChanged(oldParent);
if (_oldWindow is { } oldWindow)
{
oldWindow.StateChanged -= OnWindowStateChanged;
oldWindow.Closing -= OnWindowClosing;
}
var newWindow = System.Windows.Window.GetWindow(this);
if (newWindow is not null)
{
newWindow.StateChanged -= OnWindowStateChanged; // Unsafe
newWindow.StateChanged += OnWindowStateChanged;
newWindow.Closing += OnWindowClosing;
}
_oldWindow = newWindow;
ApplyDefaultWindowBorder();
}
public ApplicationTheme ApplicationTheme { get; set; } = ApplicationTheme.Unknown;
///
/// Gets the system and values in WPF units.
///
public static Thickness ResizeFrameBorderThickness => _resizeFrameBorderThickness ??= new Thickness(
SystemParameters.ResizeFrameVerticalBorderWidth,
SystemParameters.ResizeFrameHorizontalBorderHeight,
SystemParameters.ResizeFrameVerticalBorderWidth,
SystemParameters.ResizeFrameHorizontalBorderHeight);
///
/// Gets the thickness of the window's non-client frame used for maximizing the window with a custom chrome.
///
///
/// If you use a to extend the client area of a window to the non-client area, you need
/// to handle the edge margin issue when the window is maximized. Use this property to get the correct margin value
/// when the window is maximized, so that when the window is maximized, the client area can completely cover the
/// screen client area by no less than a single pixel at any DPI. The method
/// cannot obtain this value directly.
///
public Thickness WindowChromeNonClientFrameThickness => _windowChromeNonClientFrameThickness ??= new Thickness(
ClientAreaBorder.ResizeFrameBorderThickness.Left,
ClientAreaBorder.ResizeFrameBorderThickness.Top,
ClientAreaBorder.ResizeFrameBorderThickness.Right,
ClientAreaBorder.ResizeFrameBorderThickness.Bottom);
}