diff --git a/EleCho.WpfSuite.FluentDesign/EleCho.WpfSuite.FluentDesign.csproj b/EleCho.WpfSuite.FluentDesign/EleCho.WpfSuite.FluentDesign.csproj
index 7b039d2..3521cc7 100644
--- a/EleCho.WpfSuite.FluentDesign/EleCho.WpfSuite.FluentDesign.csproj
+++ b/EleCho.WpfSuite.FluentDesign/EleCho.WpfSuite.FluentDesign.csproj
@@ -24,10 +24,6 @@
-
-
-
-
Designer
diff --git a/EleCho.WpfSuite/Controls/Border.cs b/EleCho.WpfSuite/Controls/Border.cs
index 18a3333..e28cd0e 100644
--- a/EleCho.WpfSuite/Controls/Border.cs
+++ b/EleCho.WpfSuite/Controls/Border.cs
@@ -53,7 +53,6 @@ public Geometry ContentClip
contentGeometry.Freeze();
return contentGeometry;
-
}
else
{
@@ -62,10 +61,36 @@ public Geometry ContentClip
}
///
- protected override void OnRender(DrawingContext dc)
+ protected override Size ArrangeOverride(Size finalSize)
{
SetValue(ContentClipPropertyKey, CalculateContentClip());
- base.OnRender(dc);
+
+ return base.ArrangeOverride(finalSize);
+ }
+
+ ///
+ protected override Geometry GetLayoutClip(Size layoutSlotSize)
+ {
+ var borderThickness = BorderThickness;
+ var cornerRadius = CornerRadius;
+ var renderSize = RenderSize;
+
+ if (renderSize.Width > 0 && renderSize.Height > 0)
+ {
+ var rect = new Rect(0, 0, renderSize.Width, renderSize.Height);
+ var radii = new Radii(cornerRadius, borderThickness, true);
+
+ var layoutGeometry = new StreamGeometry();
+ using StreamGeometryContext ctx = layoutGeometry.Open();
+ GenerateGeometry(ctx, rect, radii);
+
+ layoutGeometry.Freeze();
+ return layoutGeometry;
+ }
+ else
+ {
+ return base.GetLayoutClip(layoutSlotSize);
+ }
}
///
@@ -200,10 +225,10 @@ private struct Radii
{
internal Radii(CornerRadius radii, Thickness borders, bool outer)
{
- double left = 0.5 * borders.Left;
- double top = 0.5 * borders.Top;
- double right = 0.5 * borders.Right;
- double bottom = 0.5 * borders.Bottom;
+ double left = 0.5 * borders.Left;
+ double top = 0.5 * borders.Top;
+ double right = 0.5 * borders.Right;
+ double bottom = 0.5 * borders.Bottom;
if (outer)
{
diff --git a/EleCho.WpfSuite/Controls/WindowContentAdapter.cs b/EleCho.WpfSuite/Controls/WindowContentAdapter.cs
new file mode 100644
index 0000000..8f571f6
--- /dev/null
+++ b/EleCho.WpfSuite/Controls/WindowContentAdapter.cs
@@ -0,0 +1,185 @@
+// Origin code from: https://github.com/walterlv/Walterlv.Packages/blob/master/src/Themes/Walterlv.Themes.FluentDesign/Controls/ClientAreaBorder.cs
+// Changes:
+// - Inherit from Decorator instead of Border
+// - Make 'Padding' property read-only
+// - Remove cache of properties
+
+using System;
+using System.Runtime.InteropServices;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Shell;
+
+namespace EleCho.WpfSuite
+{
+ ///
+ /// If you're using to extend the interface to non-client area, then you can add this container to the content of in order to have the internal content automatically populate the client area section.
+ /// Using this container eliminates the need for the various margin adaptations done by Setter/Trigger inside the style when the window state changes.
+ ///
+ public class WindowContentAdapter : Decorator
+ {
+#pragma warning disable IDE1006 // 命名样式
+#pragma warning disable IDE0052 // 删除未读的私有成员
+ private const int SM_CXFRAME = 32;
+ private const int SM_CYFRAME = 33;
+ private const int SM_CXPADDEDBORDER = 92;
+#pragma warning restore IDE0052 // 删除未读的私有成员
+#pragma warning restore IDE1006 // 命名样式
+
+ [DllImport("User32", ExactSpelling = true)]
+ private static extern int GetSystemMetrics(int nIndex);
+
+ private Window? _nowWindow;
+
+ ///
+ /// Padding of the client area.
+ ///
+ public Thickness Padding => (Thickness)GetValue(PaddingPropertyKey.DependencyProperty);
+
+ private static readonly DependencyPropertyKey PaddingPropertyKey = DependencyProperty.RegisterReadOnly(
+ nameof(Padding), typeof(Thickness), typeof(WindowContentAdapter),
+ new FrameworkPropertyMetadata(default(Thickness), FrameworkPropertyMetadataOptions.AffectsParentMeasure | FrameworkPropertyMetadataOptions.AffectsParentArrange));
+
+ ///
+ /// DependencyProperty of .
+ ///
+ public static readonly DependencyProperty PaddingProperty = PaddingPropertyKey.DependencyProperty;
+
+ ///
+ protected override void OnVisualParentChanged(DependencyObject oldParent)
+ {
+ base.OnVisualParentChanged(oldParent);
+
+ if (_nowWindow is { } oldWindow)
+ {
+ oldWindow.StateChanged -= Window_StateChanged;
+ }
+
+ var newWindow = (Window?)Window.GetWindow(this);
+ if (newWindow is not null)
+ {
+ newWindow.StateChanged -= Window_StateChanged;
+ newWindow.StateChanged += Window_StateChanged;
+ }
+
+ _nowWindow = newWindow;
+
+ UpdatePadding(_nowWindow);
+ }
+
+ ///
+ protected override Size MeasureOverride(Size constraint)
+ {
+ var padding = Padding;
+
+ if (Child is { } child)
+ {
+ child.Measure(new Size(
+ constraint.Width - padding.Left - padding.Right,
+ constraint.Height - padding.Top - padding.Bottom));
+
+ var finalSize = new Size(
+ child.DesiredSize.Width + padding.Left + padding.Right,
+ child.DesiredSize.Height + padding.Top + padding.Bottom);
+
+ finalSize.Width = Math.Min(finalSize.Width, constraint.Width);
+ finalSize.Height = Math.Min(finalSize.Height, constraint.Height);
+
+ return finalSize;
+ }
+ else
+ {
+ var finalSize = new Size(padding.Left + padding.Right, padding.Top + padding.Bottom);
+
+ finalSize.Width = Math.Min(finalSize.Width, constraint.Width);
+ finalSize.Height = Math.Min(finalSize.Height, constraint.Height);
+
+ return finalSize;
+ }
+ }
+
+ ///
+ protected override Size ArrangeOverride(Size arrangeSize)
+ {
+ var padding = Padding;
+ if (Child is { } child)
+ {
+ child.Arrange(new Rect(
+ padding.Left,
+ padding.Top,
+ arrangeSize.Width - padding.Left - padding.Right,
+ arrangeSize.Height - padding.Top - padding.Bottom));
+ }
+ return arrangeSize;
+ }
+
+ private void UpdatePadding(Window? window)
+ {
+ if (window is null ||
+ WindowChrome.GetWindowChrome(window) is null)
+ {
+ SetValue(PaddingPropertyKey, default(Thickness));
+ return;
+ }
+
+ var padding = window.WindowState switch
+ {
+ WindowState.Maximized => WindowChromeNonClientFrameThickness,
+ _ => default,
+ };
+
+ SetValue(PaddingPropertyKey, padding);
+ }
+
+ private void Window_StateChanged(object? sender, EventArgs e)
+ {
+ if (sender is not Window window)
+ {
+ return;
+ }
+
+ UpdatePadding(window);
+ }
+
+ ///
+ /// 获取系统的 作为 WPF 单位的边框数值。
+ ///
+ private Thickness PaddedBorderThickness
+ {
+ get
+ {
+ var paddedBorder = GetSystemMetrics(SM_CXPADDEDBORDER);
+ var dpi = GetDpi();
+ var frameSize = new Size(paddedBorder, paddedBorder);
+ var frameSizeInDips = new Size(frameSize.Width / dpi.FactorX, frameSize.Height / dpi.FactorY);
+ return new Thickness(frameSizeInDips.Width, frameSizeInDips.Height, frameSizeInDips.Width, frameSizeInDips.Height);
+ }
+ }
+
+ ///
+ /// 获取系统的 和 作为 WPF 单位的边框数值。
+ ///
+ private Thickness ResizeFrameBorderThickness => new Thickness(
+ SystemParameters.ResizeFrameVerticalBorderWidth,
+ SystemParameters.ResizeFrameHorizontalBorderHeight,
+ SystemParameters.ResizeFrameVerticalBorderWidth,
+ SystemParameters.ResizeFrameHorizontalBorderHeight);
+
+ ///
+ /// 如果使用了 来制作窗口样式以将窗口客户区覆盖到非客户区,那么就需要自己来处理窗口最大化后非客户区的边缘被裁切的问题。
+ /// 使用此属性获取窗口最大化时窗口样式应该内缩的边距数值,这样在窗口最大化时客户区便可以在任何 DPI 下不差任何一个像素地完全覆盖屏幕工作区。
+ /// 方法无法直接获得这个数值。
+ ///
+ private Thickness WindowChromeNonClientFrameThickness => new Thickness(
+ ResizeFrameBorderThickness.Left + PaddedBorderThickness.Left,
+ ResizeFrameBorderThickness.Top + PaddedBorderThickness.Top,
+ ResizeFrameBorderThickness.Right + PaddedBorderThickness.Right,
+ ResizeFrameBorderThickness.Bottom + PaddedBorderThickness.Bottom);
+
+ private Dpi GetDpi() => PresentationSource.FromVisual(this) is { } source
+ ? new Dpi(
+ (int)(Dpi.Standard.X * source.CompositionTarget.TransformToDevice.M11),
+ (int)(Dpi.Standard.Y * source.CompositionTarget.TransformToDevice.M22))
+ : Dpi.System;
+ }
+}
diff --git a/EleCho.WpfSuite/Utilities/Dpi.cs b/EleCho.WpfSuite/Utilities/Dpi.cs
new file mode 100644
index 0000000..a8d5dd0
--- /dev/null
+++ b/EleCho.WpfSuite/Utilities/Dpi.cs
@@ -0,0 +1,40 @@
+using System.Runtime.InteropServices;
+
+namespace EleCho.WpfSuite
+{
+ ///
+ /// DPI
+ ///
+ /// Horizontal value
+ /// Vertical value
+ public record struct Dpi(int X, int Y)
+ {
+ ///
+ /// Horizontal factor to standard DPI
+ ///
+ public double FactorX => (double)X / Standard.X;
+
+ ///
+ /// Vertical factor to standard DPI
+ ///
+ public double FactorY => (double)Y / Standard.Y;
+
+ ///
+ /// Standard DPI
+ ///
+ public static Dpi Standard => new Dpi(96, 96);
+
+ ///
+ /// System DPI
+ ///
+ public static Dpi System => new Dpi(GetDeviceCaps(default, LOGPIXELSX), GetDeviceCaps(default, LOGPIXELSY));
+
+
+
+ const int LOGPIXELSX = 88;
+ const int LOGPIXELSY = 90;
+
+ [DllImport("Gdi32", ExactSpelling = true)]
+ private static extern int GetDeviceCaps(nint hDC, int index);
+ }
+}
diff --git a/WpfTest/MainWindow.xaml b/WpfTest/MainWindow.xaml
index 01292a9..b8ba4a1 100644
--- a/WpfTest/MainWindow.xaml
+++ b/WpfTest/MainWindow.xaml
@@ -24,28 +24,29 @@
-
-
+
+
-
-
-
+
-
-
-
+
+
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
+
-
-
+
+
-
-
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
+
+
+
+
diff --git a/WpfTest/Tests/TempPage.xaml b/WpfTest/Tests/TempPage.xaml
index a7d43b7..28eb8b0 100644
--- a/WpfTest/Tests/TempPage.xaml
+++ b/WpfTest/Tests/TempPage.xaml
@@ -251,7 +251,14 @@
-
+
+
+