DrawPath绘制圆角矩形形状不符合预期问题求助
圆角矩形打印线条异常问题
我尝试绘制圆角矩形,但打印布局时线条出现异常:错误效果为圆角处线条断裂、衔接不自然,边框粗细不均;正确效果应为圆角平滑衔接、边框线条均匀连贯的圆角矩形。
相关代码片段
边框绘制方法
private void DrawBorder(LayoutObject object, Graphics graphics) { var objectX = object.X; var objectY = object.Y; if (_rotate180) { (objectX, objectY) = object.CalculateRotation180(_layoutHeight, _layoutWidth); } // Calculate object dimensions based on graphics DPI var xDpi = objectX * graphics.DpiX; var yDpi = objectY * graphics.DpiY; var widthDpi = object.Width * graphics.DpiX; var heightDpi = object.Height * graphics.DpiY; var borderWidthX = object.BorderThickness * graphics.DpiX; var borderWidthY = object.BorderThickness * graphics.DpiY; var cornerRadiusX = object.CornerRadius * graphics.DpiX; var cornerRadiusY = object.CornerRadius * graphics.DpiY; var rectangle = new RectangleF(xDpi, yDpi, widthDpi, heightDpi); // Draw the border (with possible rounding) if (borderWidthX > 0 || borderWidthY > 0) { if (!string.IsNullOrWhiteSpace(object.BorderColor)) { var brush = object.BorderColor.FromRgbaToSolidBrush(); if (_rotate180) { graphics.RotateCenter(180.0F, rectangle); } if (object.Orientation > 0) { graphics.RotateCenter(object.Orientation, rectangle); } // Define the rectangle for drawing the border based on the original rectangle. var borderRectangle = new RectangleF(xDpi + (borderWidthX / 2), yDpi + (borderWidthY / 2), widthDpi - borderWidthX, heightDpi - borderWidthY); if (cornerRadiusX > 0 || cornerRadiusY > 0) { // Draw the rounded border. cornerRadiusX -= borderWidthX / 2; cornerRadiusY -= borderWidthY / 2; graphics.DrawRoundedCornerRectangle(borderRectangle, new Pen(brush, borderWidthX), cornerRadiusX, cornerRadiusY); } else { graphics.DrawRectangle(new Pen(brush, borderWidthX), borderRectangle.X, borderRectangle.Y, borderRectangle.Width, borderRectangle.Height); } graphics.ResetTransform(); } } }
圆角矩形绘制扩展方法
public static void DrawRoundedCornerRectangle(this Graphics graphics, RectangleF rectangle, Pen pen, float xradius, float yradius) { using (GraphicsPath path = MakeRoundedRect(rectangle, xradius, yradius)) { graphics.DrawPath(pen, path); } } private static GraphicsPath MakeRoundedRect(RectangleF rect, float xradius, float yradius) { // Make a GraphicsPath to draw the rectangle. var path = new GraphicsPath(); // Upper left corner. RectangleF corner = new RectangleF(rect.X, rect.Y, 2 * xradius, 2 * yradius); path.AddArc(corner, 180, 90); // Upper right corner. corner = new RectangleF(rect.Right - 2 * xradius, rect.Y, 2 * xradius, 2 * yradius); path.AddArc(corner, 270, 90); // Lower right corner. corner = new RectangleF(rect.Right - 2 * xradius, rect.Bottom - 2 * yradius, 2 * xradius, 2 * yradius); path.AddArc(corner, 0, 90); // Lower left corner. corner = new RectangleF(rect.X, rect.Bottom - 2 * yradius, 2 * xradius, 2 * yradius); path.AddArc(corner, 90, 90); // Join with the start point. path.CloseFigure(); return path; }
问题排查与修复方案
核心问题分析
- 旋转时机错误:在计算
borderRectangle前就对Graphics应用旋转,导致后续基于原始坐标计算的边框位置偏移,旋转后绘制的线条出现错位。 - 圆角半径无边界检查:直接减去
borderWidthX/2可能导致圆角半径为负,引发绘制异常;同时未限制圆角半径不超过矩形边长的一半,导致圆弧绘制错误。 - 线条衔接未优化:Pen未设置
LineJoin属性,圆角与直线衔接处容易出现断裂;MakeRoundedRect仅添加圆弧未显式连接直线段,路径连续性不足。 - 变换管理不严谨:直接调用
ResetTransform可能影响其他绘制操作,应使用Graphics.Save()和状态恢复来管理变换。
修复后的代码
优化后的DrawBorder方法
private void DrawBorder(LayoutObject obj, Graphics graphics) { var objectX = obj.X; var objectY = obj.Y; if (_rotate180) { (objectX, objectY) = obj.CalculateRotation180(_layoutHeight, _layoutWidth); } // 基于DPI统一计算尺寸 var xDpi = objectX * graphics.DpiX; var yDpi = objectY * graphics.DpiY; var widthDpi = obj.Width * graphics.DpiX; var heightDpi = obj.Height * graphics.DpiY; var borderWidth = obj.BorderThickness * graphics.DpiX; var cornerRadiusX = obj.CornerRadius * graphics.DpiX; var cornerRadiusY = obj.CornerRadius * graphics.DpiY; var rectangle = new RectangleF(xDpi, yDpi, widthDpi, heightDpi); if (borderWidth > 0 && !string.IsNullOrWhiteSpace(obj.BorderColor)) { var brush = obj.BorderColor.FromRgbaToSolidBrush(); // 设置Pen的线条衔接为圆角,避免断裂 var pen = new Pen(brush, borderWidth) { LineJoin = LineJoin.Round }; // 先计算正确的边框矩形,再执行旋转 var borderRectangle = new RectangleF( xDpi + borderWidth / 2, yDpi + borderWidth / 2, widthDpi - borderWidth, heightDpi - borderWidth ); // 保存图形状态,自动恢复变换 using (var state = graphics.Save()) { if (_rotate180) { graphics.RotateCenter(180.0F, rectangle); } if (obj.Orientation > 0) { graphics.RotateCenter(obj.Orientation, rectangle); } if (cornerRadiusX > 0 || cornerRadiusY > 0) { // 确保圆角半径不小于0,且不超过矩形边长的一半 var adjustedRadiusX = Math.Max(0, Math.Min(cornerRadiusX - borderWidth / 2, borderRectangle.Width / 2)); var adjustedRadiusY = Math.Max(0, Math.Min(cornerRadiusY - borderWidth / 2, borderRectangle.Height / 2)); graphics.DrawRoundedCornerRectangle(borderRectangle, pen, adjustedRadiusX, adjustedRadiusY); } else { graphics.DrawRectangle(pen, borderRectangle.X, borderRectangle.Y, borderRectangle.Width, borderRectangle.Height); } } } }
优化后的圆角路径生成方法
public static void DrawRoundedCornerRectangle(this Graphics graphics, RectangleF rectangle, Pen pen, float xradius, float yradius) { using (GraphicsPath path = MakeRoundedRect(rectangle, xradius, yradius)) { graphics.DrawPath(pen, path); } } private static GraphicsPath MakeRoundedRect(RectangleF rect, float xradius, float yradius) { var path = new GraphicsPath(); // 限制圆角半径不超过矩形对应边长的一半 xradius = Math.Min(xradius, rect.Width / 2); yradius = Math.Min(yradius, rect.Height / 2); // 左上圆角 + 上边直线 path.AddArc(rect.X, rect.Y, 2 * xradius, 2 * yradius, 180, 90); path.AddLine(rect.X + xradius, rect.Y, rect.Right - xradius, rect.Y); // 右上圆角 + 右边直线 path.AddArc(rect.Right - 2 * xradius, rect.Y, 2 * xradius, 2 * yradius, 270, 90); path.AddLine(rect.Right, rect.Y + yradius, rect.Right, rect.Bottom - yradius); // 右下圆角 + 下边直线 path.AddArc(rect.Right - 2 * xradius, rect.Bottom - 2 * yradius, 2 * xradius, 2 * yradius, 0, 90); path.AddLine(rect.Right - xradius, rect.Bottom, rect.X + xradius, rect.Bottom); // 左下圆角 + 左边直线 path.AddArc(rect.X, rect.Bottom - 2 * yradius, 2 * xradius, 2 * yradius, 90, 90); path.AddLine(rect.X, rect.Bottom - yradius, rect.X, rect.Y + yradius); path.CloseFigure(); return path; }
修复要点说明
- 调整旋转顺序:先计算边框的正确位置,再应用旋转变换,避免坐标偏移。
- 优化线条衔接:给Pen设置
LineJoin = LineJoin.Round,确保圆角与直线衔接平滑。 - 边界检查:限制圆角半径的取值范围,避免出现负数或超出矩形尺寸的情况。
- 显式路径构造:在圆角路径中添加明确的直线段,确保路径连续无断裂。
- 安全管理变换:使用
Graphics.Save()和using块自动恢复图形状态,替代直接调用ResetTransform,避免副作用。
内容的提问来源于stack exchange,提问作者Francesco Vargas
相关产品推荐
相关产品推荐

