最稳方案是用GraphicsPath构建闭合圆角路径后调用Graphics.SetClip()或设置控件Region;必须CloseFigure()且绘图后Restore()状态,否则裁剪持续生效导致后续绘制异常。
用 GraphicsPath + Region 做圆角裁剪最稳
直接说结论:c# winforms 或 gdi+ 场景下,region 本身不裁剪像素,它只控制绘制区域;真正实现“圆角图片”必须配合 graphics.setclip() 或把路径设为控件的 region。很多人卡在“图片没变圆”,其实是忘了这一步。
常见错误现象:pictureBox.Region = new Region(path) 后图片边缘还是直角 —— 因为 PictureBox 默认不响应 Region(除非 SizeMode 是 Normal 且图片未拉伸);或者用了 Graphics.DrawImage() 却没先 g.SetClip(path),结果路径白定义。
- 必须用
GraphicsPath构建圆角矩形,不能只靠Rectangle+CornerRadius(GDI+ 没这个属性) -
Region对非透明背景无效:如果原图带白边或背景色,裁剪后会露出背景,得提前处理 Alpha 通道或用CompositingMode.SourceOver - WinForms 控件设置
Region后,鼠标事件仍按原始矩形响应,圆角外点击可能意外触发
GraphicsPath.AddArc 构建圆角路径的细节
圆角不是“加个半径参数”就完事。GraphicsPath 需手动拼四段弧线和四条直线,顺序错一点,Region 就失效(比如变成空集或反向填充)。
关键点:起点必须是左上角,顺时针画;每个圆角用 AddArc 时,传入的是包围矩形(Rect),不是圆心坐标;弧度范围固定为 0→90°、90→180° 等,别写成 Math.PI 这种弧度制 —— AddArc 要的是度数。
- 左上角弧:用
new Rectangle(x, y, radius * 2, radius * 2),起始角0,扫过90 - 右上角弧:
new Rectangle(width - radius * 2, y, radius * 2, radius * 2),起始角90,扫过90 - 路径最后必须调
CloseFigure(),否则Region可能不闭合
示例片段:
var path = new GraphicsPath(); path.StartFigure(); path.AddArc(x, y, r * 2, r * 2, 0, 90); // 左上 path.AddArc(w - r * 2, y, r * 2, r * 2, 90, 90); // 右上 path.AddArc(w - r * 2, h - r * 2, r * 2, r * 2, 180, 90); // 右下 path.AddArc(x, h - r * 2, r * 2, r * 2, 270, 90); // 左下 path.CloseFigure();
WinForms 控件 Region 和离屏绘图的区别
两种路子本质不同:一个是改控件形状(UI 层),一个是改绘制输出(绘图层)。选错场景,性能和效果都打折。
给 PictureBox 直接赋 Region 看似简单,但只对 SizeMode = PictureBoxSizeMode.Normal 有效;一旦设成 StretchImage 或 Zoom,缩放发生在 Region 裁剪之后,圆角就糊了。而离屏绘图(Bitmap → Graphics → SetClip → DrawImage)全程可控,还能抗锯齿、支持 PNG 透明。
- 控件级
Region:适合静态头像、卡片容器,省 CPU,但不兼容缩放/动画 - 绘图级裁剪:适合生成缩略图、导出圆角截图,需手动管理
Bitmap生命周期,别忘了Dispose() - WPF 或 MAUI 别走这条路 —— 它们有
Clip属性或CornerRadius直接可用,GDI+ 这套是 WinForms 专属妥协方案
SetClip 后记得重置,否则后续绘图全被裁
这是最隐蔽的坑:Graphics.SetClip(path) 不是单次作用,它会持续影响之后所有 DrawXXX 调用,直到你显式 ResetClip() 或用新 Graphics 实例。很多人画完圆角图,紧接着画文字或边框,结果全被切掉一半,死活找不到原因。
尤其在 Paint 事件里,每次重绘都走同一段逻辑,漏掉 ResetClip() 就等于“越画越小”。更安全的做法是用 Save()/Restore() 配对,而不是依赖顺序。
- 推荐写法:
var state = g.Save(); g.SetClip(path); ... g.Restore(state); - 别在循环里反复
SetClip同一个路径 ——Graphics内部会累积变换,导致坐标偏移 - 调试时临时加
g.DrawPath(Pens.Red, path)看路径是否真画在预期位置,比猜强
圆角这事,核心就两条:路径得闭合、裁剪得及时还原。其它都是围绕这两点打补丁。










