上版本雖以位元運算涵蓋所有組合,但程式碼冗長且無法控制四個圓角的半徑差異。本版引入 ArcRadius 結構,各角獨立指定半徑,邏輯大幅簡化:只需檢查半徑 > 0 即繪製對應圓弧,無需列舉所有組合。
public struct ArcRadius
{
public static readonly ArcRadius Empty = new ArcRadius(0);
private int _leftTop, _rightTop, _leftBottom, _rightBottom;
public ArcRadius(int radius) : this(radius, radius, radius, radius) { }
public ArcRadius(int leftTop, int rightTop, int leftBottom, int rightBottom)
{
_leftTop = Math.Max(0, leftTop);
_rightTop = Math.Max(0, rightTop);
_leftBottom = Math.Max(0, leftBottom);
_rightBottom = Math.Max(0, rightBottom);
}
public int LeftTop { get => _leftTop; set => _leftTop = Math.Max(0, value); }
public int RightTop { get => _rightTop; set => _rightTop = Math.Max(0, value); }
public int LeftBottom { get => _leftBottom; set => _leftBottom = Math.Max(0, value); }
public int RightBottom { get => _rightBottom; set => _rightBottom = Math.Max(0, value); }
public int All
{
get => (_leftTop == _rightTop && _leftTop == _leftBottom && _leftTop == _rightBottom) ? _leftTop : -1;
set { LeftTop = RightTop = LeftBottom = RightBottom = Math.Max(0, value); }
}
public static bool operator ==(ArcRadius a, ArcRadius b)
=> a._leftTop == b._leftTop && a._rightTop == b._rightTop && a._leftBottom == b._leftBottom && a._rightBottom == b._rightBottom;
public static bool operator !=(ArcRadius a, ArcRadius b) => !(a == b);
public override string ToString() => $"{LeftTop}, {RightTop}, {LeftBottom}, {RightBottom}";
}
單一方法 CreateRoundPath 依序繞矩形四周建構路徑:
public static GraphicsPath CreateRoundPath(Rectangle rect, ArcRadius arcRadius)
{
var path = new GraphicsPath();
if (rect.Width == 0 || rect.Height == 0) return path;
// 左上角
if (arcRadius.LeftTop > 0)
path.AddArc(rect.Left, rect.Top, arcRadius.LeftTop, arcRadius.LeftTop, 180, 90);
path.AddLine(rect.Left + arcRadius.LeftTop, rect.Top, rect.Right - arcRadius.RightTop, rect.Top);
// 右上角
if (arcRadius.RightTop > 0)
path.AddArc(rect.Right - arcRadius.RightTop, rect.Top, arcRadius.RightTop, arcRadius.RightTop, -90, 90);
path.AddLine(rect.Right, rect.Top + arcRadius.RightTop, rect.Right, rect.Bottom - arcRadius.RightBottom);
// 右下角
if (arcRadius.RightBottom > 0)
path.AddArc(rect.Right - arcRadius.RightBottom, rect.Bottom - arcRadius.RightBottom, arcRadius.RightBottom, arcRadius.RightBottom, 0, 90);
path.AddLine(rect.Right - arcRadius.RightBottom, rect.Bottom, rect.Left + arcRadius.LeftBottom, rect.Bottom);
// 左下角
if (arcRadius.LeftBottom > 0)
path.AddArc(rect.Left, rect.Bottom - arcRadius.LeftBottom, arcRadius.LeftBottom, arcRadius.LeftBottom, 90, 90);
path.AddLine(rect.Left, rect.Bottom - arcRadius.LeftBottom, rect.Left, rect.Top + arcRadius.LeftTop);
path.CloseFigure();
return path;
}
關鍵優勢:路徑點序順時針連續,無需判斷組合,自動處理任意半徑組合 (含零半徑即銳角)。
支援漸層背景、邊框、內邊框三層繪製:
public static void DrawRoundRectangle(Graphics g, RoundRectangleArg arg)
{
if (g == null || arg.Rectangle.Width == 0 || arg.Rectangle.Height == 0) return;
using (var sm = new SmoothingModeGraphics(g, SmoothingMode.AntiAlias))
using (var path = CreateRoundPath(arg.Rectangle, arg.ArcRadius))
{
if (arg.DrawBackground)
{
using (var brush = new LinearGradientBrush(arg.Rectangle, arg.StartColor, arg.EndColor, arg.LinearGradientMode))
{
var blend = new ColorBlend(2) { Positions = new[] { 0f, 1f }, Colors = new[] { arg.StartColor, arg.EndColor } };
brush.InterpolationColors = blend;
g.FillPath(brush, path);
}
}
if (arg.DrawBorder)
using (var pen = new Pen(new SolidBrush(arg.BorderColor), arg.BorderWidth)) g.DrawPath(pen, path);
if (arg.DrawInnerBorder)
{
Rectangle inner = arg.Rectangle; inner.Inflate(-2, -2);
using (var innerPath = CreateRoundPath(inner, arg.ArcRadius))
using (var pen = new Pen(new SolidBrush(arg.InnerBorderColor), arg.InnerBorderWidth))
g.DrawPath(pen, innerPath);
}
}
}
var arg = new RoundRectangleArg
{
Rectangle = rect,
ArcRadius = new ArcRadius(leftTop, rightTop, leftBottom, rightBottom),
DrawBackground = true, DrawBorder = true, DrawInnerBorder = false,
StartColor = Color.Blue, EndColor = Color.LightBlue,
LinearGradientMode = LinearGradientMode.Vertical,
BorderColor = Color.DarkBlue, BorderWidth = 2
};
GraphicsPathHelper.DrawRoundRectangle(g, arg);
改進版以資料導向取代邏輯導向,程式碼從 150+ 行精簡至 50 行核心邏輯,並獲得四角獨立半徑控制能力。更符合開閉原則,易於維護與擴充。
原始碼下載:矩形圓角繪製改進版