月更
This commit is contained in:
748
AntDesignWPF/Controls/AntBorder.cs
Normal file
748
AntDesignWPF/Controls/AntBorder.cs
Normal file
@@ -0,0 +1,748 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
using AntDesignWPF.Utils;
|
||||
|
||||
namespace AntDesignWPF.Controls;
|
||||
|
||||
/// <summary>
|
||||
/// Draws a border, background, or both around another element.
|
||||
/// </summary>
|
||||
public class AntBorder : Decorator
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private StreamGeometry backgroundGeometryCache;
|
||||
|
||||
private StreamGeometry upperLeftCache;
|
||||
|
||||
private StreamGeometry upperRightCache;
|
||||
|
||||
private StreamGeometry lowerRightCache;
|
||||
|
||||
private StreamGeometry lowerLeftCache;
|
||||
|
||||
private bool useComplexRender;
|
||||
|
||||
private Pen leftPenCache;
|
||||
|
||||
private Pen topPenCache;
|
||||
|
||||
private Pen rightPenCache;
|
||||
|
||||
private Pen bottomPenCache;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public static readonly DependencyProperty BackgroundProperty = DependencyProperty.Register(
|
||||
"Background",
|
||||
typeof(Brush),
|
||||
typeof(AntBorder),
|
||||
new FrameworkPropertyMetadata(
|
||||
null,
|
||||
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender
|
||||
));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush that fills the area between the bounds of a Border.
|
||||
/// </summary>
|
||||
public Brush Background
|
||||
{
|
||||
get { return (Brush)GetValue(BackgroundProperty); }
|
||||
set { SetValue(BackgroundProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty BorderBrushProperty = DependencyProperty.Register(
|
||||
"BorderBrush",
|
||||
typeof(Brush),
|
||||
typeof(AntBorder),
|
||||
new FrameworkPropertyMetadata(
|
||||
null,
|
||||
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
||||
OnClearPenCache
|
||||
));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the brush that draws the outer border color.
|
||||
/// </summary>
|
||||
public Brush BorderBrush
|
||||
{
|
||||
get { return (Brush)GetValue(BorderBrushProperty); }
|
||||
set { SetValue(BorderBrushProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty BorderThicknessProperty = DependencyProperty.Register(
|
||||
"BorderThickness",
|
||||
typeof(Thickness),
|
||||
typeof(AntBorder),
|
||||
new FrameworkPropertyMetadata(
|
||||
new Thickness(),
|
||||
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender,
|
||||
OnClearPenCache
|
||||
),
|
||||
IsThicknessValid);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the relative thickness of a border.
|
||||
/// </summary>
|
||||
public Thickness BorderThickness
|
||||
{
|
||||
get { return (Thickness)GetValue(BorderThicknessProperty); }
|
||||
set { SetValue(BorderThicknessProperty, value); }
|
||||
}
|
||||
|
||||
private static void OnClearPenCache(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
var border = (AntBorder)d;
|
||||
border.leftPenCache = border.topPenCache = border.rightPenCache = border.bottomPenCache = null;
|
||||
}
|
||||
|
||||
private static bool IsThicknessValid(object value)
|
||||
{
|
||||
return ThicknessUtil.IsValid((Thickness)value, false, false, false, false);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register(
|
||||
"CornerRadius",
|
||||
typeof(CornerRadius),
|
||||
typeof(AntBorder),
|
||||
new FrameworkPropertyMetadata(
|
||||
new CornerRadius(),
|
||||
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender
|
||||
),
|
||||
IsCornerRadiusValid);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value that represents the degree to which the corners of a Border are rounded.
|
||||
/// </summary>
|
||||
public CornerRadius CornerRadius
|
||||
{
|
||||
get { return (CornerRadius)GetValue(CornerRadiusProperty); }
|
||||
set { SetValue(CornerRadiusProperty, value); }
|
||||
}
|
||||
|
||||
private static bool IsCornerRadiusValid(object value)
|
||||
{
|
||||
return CornerRadiusUtil.IsValid((CornerRadius)value, false, false, false, false);
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty PaddingProperty = DependencyProperty.Register(
|
||||
"Padding",
|
||||
typeof(Thickness),
|
||||
typeof(AntBorder),
|
||||
new FrameworkPropertyMetadata(
|
||||
new Thickness(),
|
||||
FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender
|
||||
),
|
||||
IsThicknessValid);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a thickness value that describes the amount of space between a border and its child element.
|
||||
/// </summary>
|
||||
public Thickness Padding
|
||||
{
|
||||
get { return (Thickness)GetValue(PaddingProperty); }
|
||||
set { SetValue(PaddingProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty BorderStyleProperty = DependencyProperty.Register(
|
||||
"BorderStyle",
|
||||
typeof(BorderStyle),
|
||||
typeof(AntBorder),
|
||||
new FrameworkPropertyMetadata(
|
||||
BorderStyle.Solid,
|
||||
FrameworkPropertyMetadataOptions.AffectsRender | FrameworkPropertyMetadataOptions.SubPropertiesDoNotAffectRender,
|
||||
OnClearPenCache
|
||||
));
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the border style.
|
||||
/// </summary>
|
||||
public BorderStyle BorderStyle
|
||||
{
|
||||
get { return (BorderStyle)GetValue(BorderStyleProperty); }
|
||||
set { SetValue(BorderStyleProperty, value); }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Overrides
|
||||
|
||||
/// <summary>
|
||||
/// Updates DesiredSize of the Border. Called by parent UIElement. This is the first pass of layout.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Border determines its desired size it needs from the specified border the child: its sizing
|
||||
/// properties, margin, and requested size.
|
||||
/// </remarks>
|
||||
/// <param name="constraint">Constraint size is an "upper limit" that the return value should not exceed.</param>
|
||||
/// <returns>The Decorator's desired size.</returns>
|
||||
protected override Size MeasureOverride(Size constraint)
|
||||
{
|
||||
var child = Child;
|
||||
var desiredSize = new Size();
|
||||
var borders = BorderThickness;
|
||||
|
||||
if (UseLayoutRounding)
|
||||
{
|
||||
var dpi = DpiUtil.GetDpi(this);
|
||||
borders = new Thickness(UIElementUtil.RoundLayoutValue(borders.Left, dpi.DpiScaleX), UIElementUtil.RoundLayoutValue(borders.Top, dpi.DpiScaleY),
|
||||
UIElementUtil.RoundLayoutValue(borders.Right, dpi.DpiScaleX), UIElementUtil.RoundLayoutValue(borders.Bottom, dpi.DpiScaleY));
|
||||
}
|
||||
|
||||
// Compute the total size required
|
||||
var borderSize = ThicknessUtil.CollapseThickness(borders);
|
||||
var paddingSize = ThicknessUtil.CollapseThickness(Padding);
|
||||
|
||||
// If we have a child
|
||||
if (child != null)
|
||||
{
|
||||
// Combine into total decorating size
|
||||
var combined = new Size(borderSize.Width + paddingSize.Width, borderSize.Height + paddingSize.Height);
|
||||
|
||||
// Remove size of border only from child's reference size.
|
||||
var childConstraint = new Size(Math.Max(0.0, constraint.Width - combined.Width),
|
||||
Math.Max(0.0, constraint.Height - combined.Height));
|
||||
|
||||
|
||||
child.Measure(childConstraint);
|
||||
var childSize = child.DesiredSize;
|
||||
|
||||
// Now use the returned size to drive our size, by adding back the margins, etc.
|
||||
desiredSize.Width = childSize.Width + combined.Width;
|
||||
desiredSize.Height = childSize.Height + combined.Height;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Combine into total decorating size
|
||||
desiredSize = new Size(borderSize.Width + paddingSize.Width, borderSize.Height + paddingSize.Height);
|
||||
}
|
||||
|
||||
return desiredSize;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Border computes the position of its single child and applies its child's alignments to the child.
|
||||
/// </summary>
|
||||
/// <param name="finalSize">The size reserved for this element by the parent</param>
|
||||
/// <returns>The actual ink area of the element, typically the same as finalSize</returns>
|
||||
protected override Size ArrangeOverride(Size finalSize)
|
||||
{
|
||||
var borders = BorderThickness;
|
||||
|
||||
if (UseLayoutRounding)
|
||||
{
|
||||
var dpi = DpiUtil.GetDpi(this);
|
||||
borders = new Thickness(UIElementUtil.RoundLayoutValue(borders.Left, dpi.DpiScaleX), UIElementUtil.RoundLayoutValue(borders.Top, dpi.DpiScaleY),
|
||||
UIElementUtil.RoundLayoutValue(borders.Right, dpi.DpiScaleX), UIElementUtil.RoundLayoutValue(borders.Bottom, dpi.DpiScaleY));
|
||||
}
|
||||
|
||||
var boundRect = new Rect(finalSize);
|
||||
var innerRect = RectUtil.Deflate(boundRect, borders);
|
||||
|
||||
// arrange child
|
||||
var child = Child;
|
||||
if (child != null)
|
||||
{
|
||||
Rect childRect = RectUtil.Deflate(innerRect, Padding);
|
||||
child.Arrange(childRect);
|
||||
}
|
||||
|
||||
var radius = CornerRadius;
|
||||
|
||||
useComplexRender = !CornerRadiusUtil.IsUniform(radius) || !ThicknessUtil.IsUniform(borders);
|
||||
backgroundGeometryCache = upperLeftCache = upperRightCache = lowerRightCache = lowerLeftCache = null;
|
||||
|
||||
if (useComplexRender)
|
||||
{
|
||||
// calculate border / background rendering geometry
|
||||
if (!DoubleUtil.IsZero(boundRect.Width) && !DoubleUtil.IsZero(boundRect.Height))
|
||||
{
|
||||
var outerRadii = new Radii(boundRect, radius, borders, true);
|
||||
|
||||
// Upper-right corner
|
||||
var radiusX = boundRect.TopRight.X - outerRadii.TopRight.X;
|
||||
var radiusY = outerRadii.RightTop.Y - boundRect.TopRight.Y;
|
||||
if (!DoubleUtil.IsZero(radiusX) || !DoubleUtil.IsZero(radiusY))
|
||||
{
|
||||
upperRightCache = GenerateRoundedGeometry(outerRadii.TopRight, outerRadii.RightTop, new Size(radiusX, radiusY));
|
||||
}
|
||||
|
||||
// Lower-right corner
|
||||
radiusX = boundRect.BottomRight.X - outerRadii.BottomRight.X;
|
||||
radiusY = boundRect.BottomRight.Y - outerRadii.RightBottom.Y;
|
||||
if (!DoubleUtil.IsZero(radiusX) || !DoubleUtil.IsZero(radiusY))
|
||||
{
|
||||
lowerRightCache = GenerateRoundedGeometry(outerRadii.RightBottom, outerRadii.BottomRight, new Size(radiusX, radiusY));
|
||||
}
|
||||
|
||||
// Lower-left corner
|
||||
radiusX = outerRadii.BottomLeft.X - boundRect.BottomLeft.X;
|
||||
radiusY = boundRect.BottomLeft.Y - outerRadii.LeftBottom.Y;
|
||||
if (!DoubleUtil.IsZero(radiusX) || !DoubleUtil.IsZero(radiusY))
|
||||
{
|
||||
lowerLeftCache = GenerateRoundedGeometry(outerRadii.BottomLeft, outerRadii.LeftBottom, new Size(radiusX, radiusY));
|
||||
}
|
||||
|
||||
// Upper-left corner
|
||||
radiusX = outerRadii.TopLeft.X - boundRect.TopLeft.X;
|
||||
radiusY = outerRadii.LeftTop.Y - boundRect.TopLeft.Y;
|
||||
if (!DoubleUtil.IsZero(radiusX) || !DoubleUtil.IsZero(radiusY))
|
||||
{
|
||||
upperLeftCache = GenerateRoundedGeometry(outerRadii.LeftTop, outerRadii.TopLeft, new Size(radiusX, radiusY));
|
||||
}
|
||||
}
|
||||
|
||||
if (!DoubleUtil.IsZero(innerRect.Width) && !DoubleUtil.IsZero(innerRect.Height))
|
||||
{
|
||||
var innerRadii = new Radii(innerRect, radius, borders, false);
|
||||
var backgroundGeometry = new StreamGeometry();
|
||||
|
||||
using (StreamGeometryContext sc = backgroundGeometry.Open())
|
||||
{
|
||||
// create the border geometry
|
||||
sc.BeginFigure(innerRadii.TopLeft, true /* is filled */, true /* is closed */);
|
||||
|
||||
// Top line
|
||||
sc.LineTo(innerRadii.TopRight, true /* is stroked */, false /* is smooth join */);
|
||||
|
||||
// Upper-right corners
|
||||
var radiusX = innerRect.TopRight.X - innerRadii.TopRight.X;
|
||||
var radiusY = innerRadii.RightTop.Y - innerRect.TopRight.Y;
|
||||
if (!DoubleUtil.IsZero(radiusX) || !DoubleUtil.IsZero(radiusY))
|
||||
{
|
||||
sc.ArcTo(innerRadii.RightTop, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true, false);
|
||||
}
|
||||
|
||||
// Right line
|
||||
sc.LineTo(innerRadii.RightBottom, true /* is stroked */, false /* is smooth join */);
|
||||
|
||||
// Lower-right corners
|
||||
radiusX = innerRect.BottomRight.X - innerRadii.BottomRight.X;
|
||||
radiusY = innerRect.BottomRight.Y - innerRadii.RightBottom.Y;
|
||||
if (!DoubleUtil.IsZero(radiusX) || !DoubleUtil.IsZero(radiusY))
|
||||
{
|
||||
sc.ArcTo(innerRadii.BottomRight, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true, false);
|
||||
}
|
||||
|
||||
// Bottom line
|
||||
sc.LineTo(innerRadii.BottomLeft, true /* is stroked */, false /* is smooth join */);
|
||||
|
||||
// Lower-left corners
|
||||
radiusX = innerRadii.BottomLeft.X - innerRect.BottomLeft.X;
|
||||
radiusY = innerRect.BottomLeft.Y - innerRadii.LeftBottom.Y;
|
||||
if (!DoubleUtil.IsZero(radiusX) || !DoubleUtil.IsZero(radiusY))
|
||||
{
|
||||
sc.ArcTo(innerRadii.LeftBottom, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true, false);
|
||||
}
|
||||
|
||||
// Left line
|
||||
sc.LineTo(innerRadii.LeftTop, true /* is stroked */, false /* is smooth join */);
|
||||
|
||||
// Upper-left corners
|
||||
radiusX = innerRadii.TopLeft.X - innerRect.TopLeft.X;
|
||||
radiusY = innerRadii.LeftTop.Y - innerRect.TopLeft.Y;
|
||||
if (!DoubleUtil.IsZero(radiusX) || !DoubleUtil.IsZero(radiusY))
|
||||
{
|
||||
sc.ArcTo(innerRadii.TopLeft, new Size(radiusX, radiusY), 0, false, SweepDirection.Clockwise, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
backgroundGeometry.Freeze();
|
||||
backgroundGeometryCache = backgroundGeometry;
|
||||
}
|
||||
}
|
||||
|
||||
return finalSize;
|
||||
}
|
||||
|
||||
protected override void OnRender(DrawingContext dc)
|
||||
{
|
||||
if (useComplexRender)
|
||||
{
|
||||
ComplexRender(dc);
|
||||
} else
|
||||
{
|
||||
SimpleRender(dc);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
private void SimpleRender(DrawingContext dc)
|
||||
{
|
||||
var useLayoutRounding = UseLayoutRounding;
|
||||
var dpi = DpiUtil.GetDpi(this);
|
||||
|
||||
Brush brush;
|
||||
var borderStyle = BorderStyle;
|
||||
|
||||
var borders = BorderThickness;
|
||||
var cornerRadius = CornerRadius;
|
||||
|
||||
var outerCornerRadius = cornerRadius.TopLeft; // Already validated that all corners have the same radius
|
||||
var roundedCorners = !DoubleUtil.IsZero(outerCornerRadius);
|
||||
|
||||
var width = RenderSize.Width;
|
||||
var height = RenderSize.Height;
|
||||
|
||||
// Draw border
|
||||
if (!ThicknessUtil.IsZero(borders) && (brush = BorderBrush) != null)
|
||||
{
|
||||
var pen = GetPen(brush, borderStyle, borders.Left, dpi.DpiScaleX, useLayoutRounding);
|
||||
var penThickness = pen.Thickness;
|
||||
|
||||
double x = penThickness * 0.5;
|
||||
var rect = new Rect(x, x, width - penThickness, height - penThickness);
|
||||
|
||||
if (roundedCorners)
|
||||
{
|
||||
dc.DrawRoundedRectangle(null, pen, rect, outerCornerRadius, outerCornerRadius);
|
||||
}
|
||||
else
|
||||
{
|
||||
dc.DrawRectangle(null, pen, rect);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw background in rectangle inside border.
|
||||
if ((brush = Background) != null)
|
||||
{
|
||||
// Intialize background
|
||||
Point ptTL, ptBR;
|
||||
|
||||
if (useLayoutRounding)
|
||||
{
|
||||
ptTL = new Point(UIElementUtil.RoundLayoutValue(borders.Left, dpi.DpiScaleX),
|
||||
UIElementUtil.RoundLayoutValue(borders.Top, dpi.DpiScaleY));
|
||||
ptBR = new Point(width - UIElementUtil.RoundLayoutValue(borders.Right, dpi.DpiScaleX),
|
||||
height - UIElementUtil.RoundLayoutValue(borders.Bottom, dpi.DpiScaleY));
|
||||
}
|
||||
else
|
||||
{
|
||||
ptTL = new Point(borders.Left, borders.Top);
|
||||
ptBR = new Point(width - borders.Right, height - borders.Bottom);
|
||||
}
|
||||
|
||||
// Do not draw background if the borders are so large that they overlap.
|
||||
if (ptBR.X > ptTL.X && ptBR.Y > ptTL.Y)
|
||||
{
|
||||
if (roundedCorners)
|
||||
{
|
||||
// Determine the inner edge radius
|
||||
var innerCornerRadius = Math.Max(0.0, outerCornerRadius - borders.Top * 0.5);
|
||||
dc.DrawRoundedRectangle(brush, null, new Rect(ptTL, ptBR), innerCornerRadius, innerCornerRadius);
|
||||
}
|
||||
else
|
||||
{
|
||||
dc.DrawRectangle(brush, null, new Rect(ptTL, ptBR));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ComplexRender(DrawingContext dc)
|
||||
{
|
||||
Brush brush;
|
||||
var width = RenderSize.Width;
|
||||
var height = RenderSize.Height;
|
||||
|
||||
//Draw border
|
||||
if (!DoubleUtil.IsZero(width) && !DoubleUtil.IsZero(height) && (brush = BorderBrush) != null)
|
||||
{
|
||||
var useLayoutRounding = UseLayoutRounding;
|
||||
var dpi = DpiUtil.GetDpi(this);
|
||||
|
||||
var borders = BorderThickness;
|
||||
var borderStyle = BorderStyle;
|
||||
var radius = CornerRadius;
|
||||
double x, y;
|
||||
|
||||
// Left Line
|
||||
if (!DoubleUtil.IsZero(borders.Left))
|
||||
{
|
||||
if (leftPenCache == null)
|
||||
{
|
||||
leftPenCache = GetPen(brush, borderStyle, borders.Left, dpi.DpiScaleX, useLayoutRounding);
|
||||
}
|
||||
|
||||
x = leftPenCache.Thickness * 0.5;
|
||||
dc.DrawLine(leftPenCache, new Point(x, radius.TopLeft), new Point(x, height - radius.BottomLeft));
|
||||
}
|
||||
|
||||
// Top Line
|
||||
if (!DoubleUtil.IsZero(borders.Top))
|
||||
{
|
||||
if (topPenCache == null)
|
||||
{
|
||||
topPenCache = GetPen(brush, borderStyle, borders.Top, dpi.DpiScaleY, useLayoutRounding);
|
||||
}
|
||||
|
||||
y = topPenCache.Thickness * 0.5;
|
||||
dc.DrawLine(topPenCache, new Point(radius.TopLeft, y), new Point(width - radius.TopRight, y));
|
||||
}
|
||||
|
||||
// Right Line
|
||||
if (!DoubleUtil.IsZero(borders.Right))
|
||||
{
|
||||
if (rightPenCache == null)
|
||||
{
|
||||
rightPenCache = GetPen(brush, borderStyle, borders.Right, dpi.DpiScaleX, useLayoutRounding);
|
||||
}
|
||||
|
||||
x = width - rightPenCache.Thickness * 0.5;
|
||||
dc.DrawLine(rightPenCache, new Point(x, radius.TopRight), new Point(x, height - radius.BottomRight));
|
||||
}
|
||||
|
||||
// Bottom Line
|
||||
if (!DoubleUtil.IsZero(borders.Bottom))
|
||||
{
|
||||
if (bottomPenCache == null)
|
||||
{
|
||||
bottomPenCache = GetPen(brush, borderStyle, borders.Bottom, dpi.DpiScaleY, useLayoutRounding);
|
||||
}
|
||||
|
||||
y = height - bottomPenCache.Thickness * 0.5;
|
||||
dc.DrawLine(bottomPenCache, new Point(radius.BottomLeft, y), new Point(width - radius.BottomRight, y));
|
||||
}
|
||||
|
||||
// Draw Rounded
|
||||
Pen pen;
|
||||
|
||||
if (upperLeftCache != null && (pen = GetMaxPen(leftPenCache, topPenCache)) != null)
|
||||
{
|
||||
dc.DrawGeometry(null, pen, upperLeftCache);
|
||||
}
|
||||
|
||||
if (upperRightCache != null && (pen = GetMaxPen(topPenCache, rightPenCache)) != null)
|
||||
{
|
||||
dc.DrawGeometry(null, pen, upperRightCache);
|
||||
}
|
||||
|
||||
if (lowerRightCache != null && (pen = GetMaxPen(rightPenCache, bottomPenCache)) != null)
|
||||
{
|
||||
dc.DrawGeometry(null, pen, lowerRightCache);
|
||||
}
|
||||
|
||||
if (lowerLeftCache != null && (pen = GetMaxPen(bottomPenCache, leftPenCache)) != null)
|
||||
{
|
||||
dc.DrawGeometry(null, pen, lowerLeftCache);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw background in rectangle inside border.
|
||||
if (backgroundGeometryCache != null && (brush = Background) != null)
|
||||
{
|
||||
dc.DrawGeometry(brush, null, backgroundGeometryCache);
|
||||
}
|
||||
}
|
||||
|
||||
private Pen GetMaxPen(Pen pen1, Pen pen2)
|
||||
{
|
||||
if (pen2 == null || (pen1 != null && pen2.Thickness < pen1.Thickness))
|
||||
{
|
||||
return pen1;
|
||||
}
|
||||
|
||||
return pen2;
|
||||
}
|
||||
|
||||
private static StreamGeometry GenerateRoundedGeometry(Point startPoint, Point endPoint, Size size)
|
||||
{
|
||||
var streamGeometry = new StreamGeometry();
|
||||
|
||||
using (StreamGeometryContext sc = streamGeometry.Open())
|
||||
{
|
||||
sc.BeginFigure(startPoint, true, false);
|
||||
sc.ArcTo(endPoint, size, 0, false, SweepDirection.Clockwise, true, false);
|
||||
}
|
||||
|
||||
streamGeometry.Freeze();
|
||||
return streamGeometry;
|
||||
}
|
||||
|
||||
private static Pen GetPen(Brush brush, BorderStyle borderStyle, double thickness, double dpi, bool useLayoutRounding)
|
||||
{
|
||||
var pen = new Pen
|
||||
{
|
||||
Brush = brush,
|
||||
DashCap = PenLineCap.Flat,
|
||||
Thickness = useLayoutRounding ? UIElementUtil.RoundLayoutValue(thickness, dpi) : thickness,
|
||||
};
|
||||
|
||||
switch (borderStyle)
|
||||
{
|
||||
case BorderStyle.Dotted:
|
||||
pen.DashStyle = new DashStyle(new double[] { 1 }, 0d);
|
||||
break;
|
||||
case BorderStyle.Dashed:
|
||||
pen.DashStyle = new DashStyle(new double[] { 4, 2 }, 0d);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (brush.IsFrozen)
|
||||
{
|
||||
pen.Freeze();
|
||||
}
|
||||
|
||||
return pen;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Structures Classes
|
||||
|
||||
private struct Radii
|
||||
{
|
||||
#region Fields
|
||||
|
||||
internal readonly Point LeftTop;
|
||||
|
||||
internal readonly Point LeftBottom;
|
||||
|
||||
internal readonly Point TopLeft;
|
||||
|
||||
internal readonly Point TopRight;
|
||||
|
||||
internal readonly Point RightTop;
|
||||
|
||||
internal readonly Point RightBottom;
|
||||
|
||||
internal readonly Point BottomRight;
|
||||
|
||||
internal readonly Point BottomLeft;
|
||||
|
||||
#endregion
|
||||
|
||||
internal Radii(Rect rect, CornerRadius radius, Thickness borders, bool outer)
|
||||
{
|
||||
var left = borders.Left * 0.5;
|
||||
var top = borders.Top * 0.5;
|
||||
var right = borders.Right * 0.5;
|
||||
var bottom = borders.Bottom * 0.5;
|
||||
|
||||
LeftTop = new Point(0d, 0d);
|
||||
LeftBottom = new Point(0d, rect.Height);
|
||||
|
||||
TopLeft = new Point(0d, 0d);
|
||||
TopRight = new Point(rect.Width, 0d);
|
||||
|
||||
RightTop = new Point(rect.Width, 0d);
|
||||
RightBottom = new Point(rect.Width, rect.Height);
|
||||
|
||||
BottomRight = new Point(rect.Width, rect.Height);
|
||||
BottomLeft = new Point(0d, rect.Height);
|
||||
|
||||
if (outer)
|
||||
{
|
||||
LeftTop.X = left;
|
||||
LeftBottom.X = left;
|
||||
|
||||
TopLeft.Y = top;
|
||||
TopRight.Y = top;
|
||||
|
||||
RightTop.X -= right;
|
||||
RightBottom.X -= right;
|
||||
|
||||
BottomLeft.Y -= bottom;
|
||||
BottomRight.Y -= bottom;
|
||||
|
||||
if (!DoubleUtil.IsZero(radius.TopLeft))
|
||||
{
|
||||
TopLeft.X = radius.TopLeft; // + left;
|
||||
LeftTop.Y = radius.TopLeft;// + top;
|
||||
}
|
||||
|
||||
if (!DoubleUtil.IsZero(radius.TopRight))
|
||||
{
|
||||
RightTop.Y = radius.TopRight;// + top;
|
||||
TopRight.X -= radius.TopRight;// + right;
|
||||
}
|
||||
|
||||
if (!DoubleUtil.IsZero(radius.BottomRight))
|
||||
{
|
||||
BottomRight.X -= radius.BottomRight;// + right;
|
||||
RightBottom.Y -= radius.BottomRight;// + bottom; ;
|
||||
}
|
||||
|
||||
if (!DoubleUtil.IsZero(radius.BottomLeft))
|
||||
{
|
||||
LeftBottom.Y -= radius.BottomLeft; // + bottom;
|
||||
BottomLeft.X = radius.BottomLeft;// + left;
|
||||
}
|
||||
} else
|
||||
{
|
||||
TopLeft.X = Math.Max(0.0, radius.TopLeft - left);
|
||||
LeftTop.Y = Math.Max(0.0, radius.TopLeft - top);
|
||||
|
||||
RightTop.Y = Math.Max(0.0, radius.TopRight - top);
|
||||
TopRight.X -= Math.Max(0.0, radius.TopRight - right);
|
||||
|
||||
BottomRight.X -= Math.Max(0.0, radius.BottomRight - right);
|
||||
RightBottom.Y -= Math.Max(0.0, radius.BottomRight - bottom);
|
||||
|
||||
LeftBottom.Y -= Math.Max(0.0, radius.BottomLeft - bottom);
|
||||
BottomLeft.X = Math.Max(0.0, radius.BottomLeft - left);
|
||||
}
|
||||
|
||||
// check keypoints for overlap and resolve by partitioning corners according to
|
||||
// the percentage of each one.
|
||||
|
||||
// top edge
|
||||
if (TopLeft.X > TopRight.X)
|
||||
{
|
||||
var v = TopLeft.X / (TopLeft.X + rect.Width - TopRight.X) * rect.Width;
|
||||
TopLeft.X = v;
|
||||
TopRight.X = v;
|
||||
}
|
||||
|
||||
// right edge
|
||||
if (RightTop.Y > RightBottom.Y)
|
||||
{
|
||||
var v = RightTop.Y / (RightTop.Y + rect.Height - RightBottom.Y) * rect.Height;
|
||||
RightTop.Y = v;
|
||||
RightBottom.Y = v;
|
||||
}
|
||||
|
||||
// bottom edge
|
||||
if (BottomRight.X < BottomLeft.X)
|
||||
{
|
||||
var v = BottomLeft.X / (BottomLeft.X + rect.Width - BottomRight.X) * rect.Width;
|
||||
BottomRight.X = v;
|
||||
BottomLeft.X = v;
|
||||
}
|
||||
|
||||
// left edge
|
||||
if (LeftBottom.Y < LeftTop.Y)
|
||||
{
|
||||
var v = LeftTop.Y / (LeftTop.Y + rect.Height - LeftBottom.Y) * rect.Height;
|
||||
LeftBottom.Y = v;
|
||||
LeftTop.Y = v;
|
||||
}
|
||||
|
||||
// Apply offset
|
||||
var offset = new Vector(rect.TopLeft.X, rect.TopLeft.Y);
|
||||
|
||||
LeftTop += offset;
|
||||
LeftBottom += offset;
|
||||
|
||||
TopRight += offset;
|
||||
TopLeft += offset;
|
||||
|
||||
RightTop += offset;
|
||||
RightBottom += offset;
|
||||
|
||||
BottomRight += offset;
|
||||
BottomLeft += offset;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Private Structures Classes
|
||||
}
|
||||
Reference in New Issue
Block a user