chore: add avalonia DashedBorder.

This commit is contained in:
NaBian 2024-07-28 15:03:30 +08:00
parent d704338680
commit a6c4f7b2f3
5 changed files with 672 additions and 36 deletions

View File

@ -0,0 +1,607 @@
using System;
using Avalonia;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.Media;
using HandyControl.Tools;
using HandyControl.Tools.Extension;
namespace HandyControl.Controls;
public class DashedBorder : Decorator
{
public static readonly StyledProperty<double> BorderDashThicknessProperty =
AvaloniaProperty.Register<DashedBorder, double>(nameof(BorderDashThickness));
public static readonly StyledProperty<AvaloniaList<double>?> BorderDashArrayProperty =
AvaloniaProperty.Register<DashedBorder, AvaloniaList<double>?>(nameof(BorderDashArray));
public static readonly StyledProperty<PenLineCap> BorderDashCapProperty =
AvaloniaProperty.Register<DashedBorder, PenLineCap>(nameof(BorderDashCap));
public static readonly StyledProperty<double> BorderDashOffsetProperty =
AvaloniaProperty.Register<DashedBorder, double>(nameof(BorderDashOffset));
public static readonly StyledProperty<Thickness> BorderThicknessProperty =
Border.BorderThicknessProperty.AddOwner<DashedBorder>();
public static readonly StyledProperty<CornerRadius> CornerRadiusProperty =
Border.CornerRadiusProperty.AddOwner<DashedBorder>();
public static readonly StyledProperty<IBrush?> BorderBrushProperty =
Border.BorderBrushProperty.AddOwner<DashedBorder>();
public static readonly StyledProperty<IBrush?> BackgroundProperty =
Border.BackgroundProperty.AddOwner<DashedBorder>();
private Thickness? _layoutThickness;
private double _scale;
private bool _useComplexRendering;
private StreamGeometry? _backgroundGeometryCache;
private StreamGeometry? _borderGeometryCache;
private Pen? _leftPenCache;
private Pen? _rightPenCache;
private Pen? _topPenCache;
private Pen? _bottomPenCache;
private Pen? _geometryPenCache;
static DashedBorder()
{
AffectsMeasure<DashedBorder>(
BorderDashThicknessProperty,
BorderThicknessProperty
);
AffectsRender<DashedBorder>(
BorderDashArrayProperty,
BorderDashCapProperty,
BorderDashOffsetProperty,
CornerRadiusProperty,
BorderBrushProperty,
BackgroundProperty
);
}
public double BorderDashThickness
{
get => GetValue(BorderDashThicknessProperty);
set => SetValue(BorderDashThicknessProperty, value);
}
public AvaloniaList<double>? BorderDashArray
{
get => GetValue(BorderDashArrayProperty);
set => SetValue(BorderDashArrayProperty, value);
}
public PenLineCap BorderDashCap
{
get => GetValue(BorderDashCapProperty);
set => SetValue(BorderDashCapProperty, value);
}
public double BorderDashOffset
{
get => GetValue(BorderDashOffsetProperty);
set => SetValue(BorderDashOffsetProperty, value);
}
public Thickness BorderThickness
{
get => GetValue(BorderThicknessProperty);
set => SetValue(BorderThicknessProperty, value);
}
public CornerRadius CornerRadius
{
get => GetValue(CornerRadiusProperty);
set => SetValue(CornerRadiusProperty, value);
}
public IBrush? BorderBrush
{
get => GetValue(BorderBrushProperty);
set => SetValue(BorderBrushProperty, value);
}
public IBrush? Background
{
get => GetValue(BackgroundProperty);
set => SetValue(BackgroundProperty, value);
}
private Thickness LayoutThickness
{
get
{
VerifyScale();
if (_layoutThickness.HasValue)
{
return _layoutThickness.Value;
}
Thickness thickness = BorderThickness;
if (UseLayoutRounding)
{
thickness = LayoutHelper.RoundLayoutThickness(thickness, _scale, _scale);
}
_layoutThickness = thickness;
return _layoutThickness.Value;
}
}
private void VerifyScale()
{
double layoutScale = LayoutHelper.GetLayoutScale(this);
if (MathHelper.AreClose(layoutScale, this._scale))
{
return;
}
_scale = layoutScale;
_layoutThickness = new Thickness?();
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
string propertyName = change.Property.Name;
if (string.Equals(propertyName, nameof(BorderThickness)) ||
string.Equals(propertyName, nameof(BorderDashThickness)) ||
string.Equals(propertyName, nameof(BorderBrush)) ||
string.Equals(propertyName, nameof(Background)) ||
string.Equals(propertyName, nameof(BorderDashArray)) ||
string.Equals(propertyName, nameof(BorderDashCap)) ||
string.Equals(propertyName, nameof(BorderDashOffset)))
{
ClearCache();
}
}
private void ClearCache()
{
_leftPenCache = null;
_rightPenCache = null;
_topPenCache = null;
_bottomPenCache = null;
_geometryPenCache = null;
_backgroundGeometryCache = null;
_borderGeometryCache = null;
}
public override void Render(DrawingContext context)
{
var radii = CornerRadius;
var borderBrush = BorderBrush;
var background = Background;
var borderThickness = BorderThickness;
bool useLayoutRounding = UseLayoutRounding;
bool uniformCorners = AreUniformCorners(radii);
_useComplexRendering = !uniformCorners;
if (!_useComplexRendering && borderBrush != null)
{
_useComplexRendering = !MathHelper.IsZero(radii.TopLeft) && !borderThickness.IsUniform;
}
if (_useComplexRendering)
{
var boundRect = new Rect(Bounds.Size);
var innerRect = boundRect.Deflate(borderThickness);
var innerRadii = new Radii(radii, borderThickness, false);
if (_backgroundGeometryCache == null &&
!MathHelper.IsZero(innerRect.Width) &&
!MathHelper.IsZero(innerRect.Height))
{
var backgroundGeometry = new StreamGeometry();
using (var ctx = backgroundGeometry.Open())
{
GenerateGeometry(ctx, innerRect, innerRadii);
}
_backgroundGeometryCache = backgroundGeometry;
}
else
{
_backgroundGeometryCache = null;
}
if (_borderGeometryCache == null &&
!MathHelper.IsZero(boundRect.Width) &&
!MathHelper.IsZero(boundRect.Height))
{
var outerRadii = new Radii(radii, borderThickness, true);
var borderGeometry = new StreamGeometry();
using (var ctx = borderGeometry.Open())
{
GenerateGeometry(ctx, boundRect, outerRadii);
}
_borderGeometryCache = borderGeometry;
}
else
{
_borderGeometryCache = null;
}
}
else
{
_backgroundGeometryCache = null;
_borderGeometryCache = null;
}
if (_useComplexRendering)
{
if (_borderGeometryCache != null && borderBrush != null)
{
_geometryPenCache ??= new Pen
{
Brush = borderBrush,
Thickness = BorderDashThickness,
LineCap = BorderDashCap,
DashStyle = new DashStyle(BorderDashArray, BorderDashOffset)
};
context.DrawGeometry(null, _geometryPenCache, _borderGeometryCache);
}
if (_backgroundGeometryCache != null && background != null)
{
context.DrawGeometry(background, null, _backgroundGeometryCache);
}
}
else
{
var cornerRadius = CornerRadius;
double outerCornerRadius = cornerRadius.TopLeft;
bool roundedCorners = !MathHelper.IsZero(outerCornerRadius);
if (!borderThickness.IsZero() && borderBrush != null)
{
_leftPenCache ??= new Pen
{
Brush = borderBrush,
Thickness = LayoutThickness.Left,
LineCap = BorderDashCap,
DashStyle = new DashStyle(BorderDashArray, BorderDashOffset)
};
double halfThickness;
var renderSize = Bounds.Size;
if (borderThickness.IsUniform())
{
halfThickness = _leftPenCache.Thickness * 0.5;
var rect = new Rect(
new Point(halfThickness, halfThickness),
new Point(renderSize.Width - halfThickness, renderSize.Height - halfThickness)
);
if (roundedCorners)
{
context.DrawRectangle(null, _leftPenCache, rect, outerCornerRadius, outerCornerRadius);
}
else
{
context.DrawRectangle(null, _leftPenCache, rect);
}
}
else
{
if (MathHelper.GreaterThan(borderThickness.Left, 0))
{
halfThickness = _leftPenCache.Thickness * 0.5;
context.DrawLine(
_leftPenCache,
new Point(halfThickness, 0),
new Point(halfThickness, renderSize.Height)
);
}
if (MathHelper.GreaterThan(borderThickness.Right, 0))
{
_rightPenCache ??= new Pen
{
Brush = borderBrush,
Thickness = LayoutThickness.Right,
LineCap = BorderDashCap,
DashStyle = new DashStyle(BorderDashArray, BorderDashOffset)
};
halfThickness = _rightPenCache.Thickness * 0.5;
context.DrawLine(
_rightPenCache,
new Point(renderSize.Width - halfThickness, 0),
new Point(renderSize.Width - halfThickness, renderSize.Height)
);
}
if (MathHelper.GreaterThan(borderThickness.Top, 0))
{
_topPenCache ??= new Pen
{
Brush = borderBrush,
Thickness = LayoutThickness.Top,
LineCap = BorderDashCap,
DashStyle = new DashStyle(BorderDashArray, BorderDashOffset)
};
halfThickness = _topPenCache.Thickness * 0.5;
context.DrawLine(
_topPenCache,
new Point(0, halfThickness),
new Point(renderSize.Width, halfThickness)
);
}
if (MathHelper.GreaterThan(borderThickness.Bottom, 0))
{
_bottomPenCache ??= new Pen
{
Brush = borderBrush,
Thickness = LayoutThickness.Bottom,
LineCap = BorderDashCap,
DashStyle = new DashStyle(BorderDashArray, BorderDashOffset)
};
halfThickness = _bottomPenCache.Thickness * 0.5;
context.DrawLine(
_bottomPenCache,
new Point(0, renderSize.Height - halfThickness),
new Point(renderSize.Width, renderSize.Height - halfThickness)
);
}
}
}
if (background != null)
{
Point pointLeftTop, pointRightBottom;
if (useLayoutRounding)
{
pointLeftTop = new Point(LayoutThickness.Left, LayoutThickness.Top);
pointRightBottom = new Point(
Bounds.Size.Width - LayoutThickness.Right,
Bounds.Size.Height - LayoutThickness.Bottom
);
}
else
{
pointLeftTop = new Point(borderThickness.Left, borderThickness.Top);
pointRightBottom = new Point(
Bounds.Size.Width - borderThickness.Right,
Bounds.Size.Height - borderThickness.Bottom
);
}
if (pointRightBottom.X > pointLeftTop.X && pointRightBottom.Y > pointLeftTop.Y)
{
if (roundedCorners)
{
var innerRadii = new Radii(cornerRadius, borderThickness, false);
double innerCornerRadius = innerRadii._topLeft;
context.DrawRectangle(
brush: background,
pen: null,
rect: new Rect(pointLeftTop, pointRightBottom),
radiusX: innerCornerRadius,
radiusY: innerCornerRadius
);
}
else
{
context.DrawRectangle(
brush: background,
pen: null,
rect: new Rect(pointLeftTop, pointRightBottom)
);
}
}
}
}
}
protected override Size MeasureOverride(Size availableSize)
{
return LayoutHelper.MeasureChild(Child, availableSize, Padding, BorderThickness);
}
protected override Size ArrangeOverride(Size finalSize)
{
return LayoutHelper.ArrangeChild(Child, finalSize, Padding, BorderThickness);
}
private static bool AreUniformCorners(CornerRadius borderRadii)
{
double topLeft = borderRadii.TopLeft;
return MathHelper.AreClose(topLeft, borderRadii.TopRight) &&
MathHelper.AreClose(topLeft, borderRadii.BottomLeft) &&
MathHelper.AreClose(topLeft, borderRadii.BottomRight);
}
private static void GenerateGeometry(StreamGeometryContext ctx, Rect rect, Radii radii)
{
var topLeft = new Point(radii._leftTop, 0);
var topRight = new Point(rect.Width - radii._rightTop, 0);
var rightTop = new Point(rect.Width, radii._topRight);
var rightBottom = new Point(rect.Width, rect.Height - radii._bottomRight);
var bottomRight = new Point(rect.Width - radii._rightBottom, rect.Height);
var bottomLeft = new Point(radii._leftBottom, rect.Height);
var leftBottom = new Point(0, rect.Height - radii._bottomLeft);
var leftTop = new Point(0, radii._topLeft);
// top edge
if (topLeft.X > topRight.X)
{
double v = radii._leftTop / (radii._leftTop + radii._rightTop) * rect.Width;
topLeft = topLeft.WithX(v);
topRight = topRight.WithX(v);
}
// right edge
if (rightTop.Y > rightBottom.Y)
{
double v = radii._topRight / (radii._topRight + radii._bottomRight) * rect.Height;
rightTop = rightTop.WithY(v);
rightBottom = rightBottom.WithY(v);
}
// bottom edge
if (bottomRight.X < bottomLeft.X)
{
double v = radii._leftBottom / (radii._leftBottom + radii._rightBottom) * rect.Width;
bottomRight = bottomRight.WithX(v);
bottomLeft = bottomLeft.WithX(v);
}
// left edge
if (leftBottom.Y < leftTop.Y)
{
double v = radii._topLeft / (radii._topLeft + radii._bottomLeft) * rect.Height;
leftBottom = leftBottom.WithY(v);
leftTop = leftTop.WithY(v);
}
// add on offsets
var offset = new Vector(rect.TopLeft.X, rect.TopLeft.Y);
topLeft += offset;
topRight += offset;
rightTop += offset;
rightBottom += offset;
bottomRight += offset;
bottomLeft += offset;
leftBottom += offset;
leftTop += offset;
// create the border geometry
ctx.BeginFigure(topLeft, true);
// Top line
ctx.LineTo(topRight, true);
// Upper-right corner
double radiusX = rect.TopRight.X - topRight.X;
double radiusY = rightTop.Y - rect.TopRight.Y;
if (!MathHelper.IsZero(radiusX) || !MathHelper.IsZero(radiusY))
{
ctx.ArcTo(rightTop, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true);
}
// Right line
ctx.LineTo(rightBottom, true);
// Lower-right corner
radiusX = rect.BottomRight.X - bottomRight.X;
radiusY = rect.BottomRight.Y - rightBottom.Y;
if (!MathHelper.IsZero(radiusX) || !MathHelper.IsZero(radiusY))
{
ctx.ArcTo(bottomRight, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true);
}
// Bottom line
ctx.LineTo(bottomLeft, true);
// Lower-left corner
radiusX = bottomLeft.X - rect.BottomLeft.X;
radiusY = rect.BottomLeft.Y - leftBottom.Y;
if (!MathHelper.IsZero(radiusX) || !MathHelper.IsZero(radiusY))
{
ctx.ArcTo(leftBottom, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true);
}
// Left line
ctx.LineTo(leftTop, true);
// Upper-left corner
radiusX = topLeft.X - rect.TopLeft.X;
radiusY = leftTop.Y - rect.TopLeft.Y;
if (!MathHelper.IsZero(radiusX) || !MathHelper.IsZero(radiusY))
{
ctx.ArcTo(topLeft, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true);
}
ctx.EndFigure(true);
}
private readonly 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;
if (outer)
{
if (MathHelper.IsZero(radii.TopLeft))
{
_leftTop = _topLeft = 0.0;
}
else
{
_leftTop = radii.TopLeft + left;
_topLeft = radii.TopLeft + top;
}
if (MathHelper.IsZero(radii.TopRight))
{
_topRight = _rightTop = 0.0;
}
else
{
_topRight = radii.TopRight + top;
_rightTop = radii.TopRight + right;
}
if (MathHelper.IsZero(radii.BottomRight))
{
_rightBottom = _bottomRight = 0.0;
}
else
{
_rightBottom = radii.BottomRight + right;
_bottomRight = radii.BottomRight + bottom;
}
if (MathHelper.IsZero(radii.BottomLeft))
{
_bottomLeft = _leftBottom = 0.0;
}
else
{
_bottomLeft = radii.BottomLeft + bottom;
_leftBottom = radii.BottomLeft + left;
}
}
else
{
_leftTop = Math.Max(0.0, radii.TopLeft - left);
_topLeft = Math.Max(0.0, radii.TopLeft - top);
_topRight = Math.Max(0.0, radii.TopRight - top);
_rightTop = Math.Max(0.0, radii.TopRight - right);
_rightBottom = Math.Max(0.0, radii.BottomRight - right);
_bottomRight = Math.Max(0.0, radii.BottomRight - bottom);
_bottomLeft = Math.Max(0.0, radii.BottomLeft - bottom);
_leftBottom = Math.Max(0.0, radii.BottomLeft - left);
}
}
internal readonly double _leftTop;
internal readonly double _topLeft;
internal readonly double _topRight;
internal readonly double _rightTop;
internal readonly double _rightBottom;
internal readonly double _bottomRight;
internal readonly double _bottomLeft;
internal readonly double _leftBottom;
}
}

View File

@ -44,21 +44,23 @@ public class UniformSpacingPanel : Panel
AvaloniaProperty.Register<UniformSpacingPanel, VerticalAlignment?>(nameof(ItemVerticalAlignment),
defaultValue: VerticalAlignment.Stretch);
private Orientation _orientation;
static UniformSpacingPanel()
{
AffectsMeasure<StackPanel>(OrientationProperty);
AffectsMeasure<StackPanel>(ChildWrappingProperty);
AffectsMeasure<StackPanel>(SpacingProperty);
AffectsMeasure<StackPanel>(HorizontalSpacingProperty);
AffectsMeasure<StackPanel>(VerticalSpacingProperty);
AffectsMeasure<StackPanel>(ItemWidthProperty);
AffectsMeasure<StackPanel>(ItemHeightProperty);
AffectsMeasure<StackPanel>(ItemHorizontalAlignmentProperty);
AffectsMeasure<StackPanel>(ItemVerticalAlignmentProperty);
AffectsMeasure<StackPanel>(
OrientationProperty,
ChildWrappingProperty,
SpacingProperty,
HorizontalSpacingProperty,
VerticalSpacingProperty,
ItemWidthProperty,
ItemHeightProperty,
ItemHorizontalAlignmentProperty,
ItemVerticalAlignmentProperty
);
}
private static double CoerceLength(AvaloniaObject _, double length) => length < 0 ? 0 : length;
public Orientation Orientation
{
get => GetValue(OrientationProperty);
@ -113,12 +115,23 @@ public class UniformSpacingPanel : Panel
set => SetValue(ItemVerticalAlignmentProperty, value);
}
private static double CoerceLength(AvaloniaObject _, double length) => length < 0 ? 0 : length;
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
if (string.Equals(change.Property.Name, nameof(Orientation)))
{
_orientation = change.GetNewValue<Orientation>();
}
}
protected override Size MeasureOverride(Size availableSize)
{
var orientation = Orientation;
var curLineSize = new PanelUvSize(orientation);
var panelSize = new PanelUvSize(orientation);
var uvConstraint = new PanelUvSize(orientation, availableSize);
var curLineSize = new PanelUvSize(_orientation);
var panelSize = new PanelUvSize(_orientation);
var uvConstraint = new PanelUvSize(_orientation, availableSize);
double itemWidth = ItemWidth;
double itemHeight = ItemHeight;
bool itemWidthSet = !double.IsNaN(itemWidth);
@ -138,9 +151,9 @@ public class UniformSpacingPanel : Panel
if (childWrapping == VisualWrapping.NoWrap)
{
var layoutSlotSize = new PanelUvSize(orientation, availableSize);
var layoutSlotSize = new PanelUvSize(_orientation, availableSize);
if (orientation == Orientation.Horizontal)
if (_orientation == Orientation.Horizontal)
{
layoutSlotSize.V = double.PositiveInfinity;
}
@ -166,7 +179,7 @@ public class UniformSpacingPanel : Panel
child.Measure(new Size(layoutSlotSize.Width, layoutSlotSize.Height));
var sz = new PanelUvSize(
orientation,
_orientation,
itemWidthSet ? itemWidth : child.DesiredSize.Width,
itemHeightSet ? itemHeight : child.DesiredSize.Height);
@ -195,7 +208,7 @@ public class UniformSpacingPanel : Panel
child.Measure(childConstraint);
var sz = new PanelUvSize(
orientation,
_orientation,
itemWidthSet ? itemWidth : child.DesiredSize.Width,
itemHeightSet ? itemHeight : child.DesiredSize.Height);
@ -223,17 +236,16 @@ public class UniformSpacingPanel : Panel
protected override Size ArrangeOverride(Size finalSize)
{
var orientation = Orientation;
int firstInLine = 0;
double itemWidth = ItemWidth;
double itemHeight = ItemHeight;
double accumulatedV = 0;
double itemU = orientation == Orientation.Horizontal ? itemWidth : itemHeight;
var curLineSize = new PanelUvSize(orientation);
var uvFinalSize = new PanelUvSize(orientation, finalSize);
double itemU = _orientation == Orientation.Horizontal ? itemWidth : itemHeight;
var curLineSize = new PanelUvSize(_orientation);
var uvFinalSize = new PanelUvSize(_orientation, finalSize);
bool itemWidthSet = !double.IsNaN(itemWidth);
bool itemHeightSet = !double.IsNaN(itemHeight);
bool useItemU = orientation == Orientation.Horizontal ? itemWidthSet : itemHeightSet;
bool useItemU = _orientation == Orientation.Horizontal ? itemWidthSet : itemHeightSet;
var childWrapping = ChildWrapping;
var spacingSize = GetSpacingSize();
@ -250,14 +262,13 @@ public class UniformSpacingPanel : Panel
var child = Children[i];
var sz = new PanelUvSize(
orientation,
_orientation,
itemWidthSet ? itemWidth : child.DesiredSize.Width,
itemHeightSet ? itemHeight : child.DesiredSize.Height);
if (!isFirst && MathHelper.GreaterThan(curLineSize.U + sz.U + spacingSize.U, uvFinalSize.U))
{
ArrangeWrapLine(orientation, accumulatedV, curLineSize.V, firstInLine, i, useItemU, itemU,
spacingSize.U);
ArrangeWrapLine(accumulatedV, curLineSize.V, firstInLine, i, useItemU, itemU, spacingSize.U);
accumulatedV += curLineSize.V + spacingSize.V;
curLineSize = sz;
@ -275,7 +286,7 @@ public class UniformSpacingPanel : Panel
if (firstInLine < Children.Count)
{
ArrangeWrapLine(orientation, accumulatedV, curLineSize.V, firstInLine, Children.Count, useItemU, itemU,
ArrangeWrapLine(accumulatedV, curLineSize.V, firstInLine, Children.Count, useItemU, itemU,
spacingSize.U);
}
}
@ -331,7 +342,6 @@ public class UniformSpacingPanel : Panel
}
private void ArrangeWrapLine(
Orientation orientation,
double v,
double lineV,
int start,
@ -341,13 +351,13 @@ public class UniformSpacingPanel : Panel
double spacing)
{
double u = 0;
bool isHorizontal = orientation == Orientation.Horizontal;
bool isHorizontal = _orientation == Orientation.Horizontal;
for (int i = start; i < end; i++)
{
var child = Children[i];
var childSize = new PanelUvSize(orientation, child.DesiredSize);
var childSize = new PanelUvSize(_orientation, child.DesiredSize);
double layoutSlotU = useItemU ? itemU : childSize.U;
child.Arrange(isHorizontal ? new Rect(u, v, layoutSlotU, lineV) : new Rect(v, u, lineV, layoutSlotU));

View File

@ -0,0 +1,22 @@
using Avalonia;
namespace HandyControl.Tools.Extension;
internal static class ValueExtension
{
public static Thickness Add(this Thickness a, Thickness b) =>
new(a.Left + b.Left, a.Top + b.Top, a.Right + b.Right, a.Bottom + b.Bottom);
public static bool IsZero(this Thickness thickness) =>
MathHelper.IsZero(thickness.Left)
&& MathHelper.IsZero(thickness.Top)
&& MathHelper.IsZero(thickness.Right)
&& MathHelper.IsZero(thickness.Bottom);
public static bool IsUniform(this Thickness thickness) =>
MathHelper.AreClose(thickness.Left, thickness.Top)
&& MathHelper.AreClose(thickness.Left, thickness.Right)
&& MathHelper.AreClose(thickness.Left, thickness.Bottom);
public static bool IsNaN(this double value) => double.IsNaN(value);
}

View File

@ -6,6 +6,8 @@ internal static class MathHelper
{
public const double Epsilon = 2.2204460492503131e-016;
public static bool IsZero(double value) => Math.Abs(value) < 10.0 * Epsilon;
public static bool AreClose(double value1, double value2) =>
// ReSharper disable once CompareOfFloatsByEqualityOperator
value1 == value2 || IsVerySmall(value1 - value2);

View File

@ -359,11 +359,6 @@ public class DashedBorder : Decorator
using (var ctx = borderGeometry.Open())
{
GenerateGeometry(ctx, boundRect, outerRadii);
if (backgroundGeometry != null)
{
GenerateGeometry(ctx, innerRect, innerRadii);
}
}
borderGeometry.Freeze();