导读:本期聚焦于小伙伴创作的《C# GDI+绘图方法有哪些?C#如何实现图形绘制与自定义控件重绘》,敬请观看详情。在WinForms界面开发中,直接在窗口上画线条或填充形状常让初学者困惑于从哪入手。GDI+是.NET提供的二维图形接口,核心类为Graphics,它封装了设备上下文与绘制命令。实际绘图不能新建Graphics对象随意画,必须通过Paint事件的e.Graphics或控件CreateGraphics获取。绘制流程包括创建画刷画笔、调用DrawLine、FillRectangle等方法、处理坐标变换。若想做实时曲线或棋盘,需重写OnPaint并利用双缓冲减少闪烁。理解Graphics生命周期与无效区域机制,才能写出稳定不卡顿的绘图代码。

在C#的Windows Forms开发中,GDI+是最基础也最常用的二维图形绘制技术。借助GDI+,开发者可以在窗体、Panel或者自定义控件上绘制直线、矩形、椭圆、文本以及图像,实现数据可视化、报表设计和游戏原型等功能。理解GDI+的体系结构,是掌握C#图形编程的第一步。

C# GDI+绘图方法有哪些?C#如何实现图形绘制与自定义控件重绘

一、GDI+核心对象与获取Graphics的方式

GDI+的核心类是System.Drawing.Graphics,它相当于一块画布,所有的绘制操作都通过它的方法完成。很多新手容易犯的错误是直接使用new Graphics(),实际上Graphics没有公开构造函数,必须借助已有对象的上下文来创建。

最常用的获取方式有三种。第一是在控件的Paint事件中使用e.Graphics,这是最推荐的做法,因为系统会自动管理绘制时机和裁剪区域。第二是调用控件的CreateGraphics()方法,适合临时绘制,但窗体被遮挡或重绘时会丢失内容。第三是通过Graphics.FromImage()从位图创建,用于离屏绘制或保存图片。下面的代码展示了Paint事件中的基本用法:

using System.Drawing;
using System.Windows.Forms;

public class DrawForm : Form
{
    public DrawForm()
    {
        this.Paint += DrawForm_Paint;
    }

    private void DrawForm_Paint(object sender, PaintEventArgs e)
    {
        // 通过事件参数获取Graphics对象
        Graphics g = e.Graphics;
        // 创建红色画笔
        Pen pen = new Pen(Color.Red, 2);
        // 绘制一条从(10,10)到(200,100)的直线
        g.DrawLine(pen, 10, 10, 200, 100);
        pen.Dispose();
    }
}

1.1 画笔与画刷的区别

Pen用于绘制线条和轮廓,可以设置颜色、宽度、虚线样式;Brush用于填充封闭区域,例如SolidBrush实心填充、LinearGradientBrush渐变填充。混淆两者是常见误区,Draw方法用Pen,Fill方法用Brush。

在使用完Pen和Brush后应当调用Dispose()释放非托管资源,或者使用using语句块。否则长时间运行可能导致GDI对象泄漏,表现为界面绘制变慢甚至系统报资源不足。

二、常见图形绘制方法实践

Graphics类提供了一组直观的绘制函数,例如DrawRectangleFillEllipseDrawString等。坐标系默认以控件左上角为原点,向右为X正方向,向下为Y正方向,这与数学笛卡尔坐标系不同,需要适应。

下面示例在Panel上绘制一个带渐变填充的矩形和一段文字,并演示了如何安全释放资源:

private void panel1_Paint(object sender, PaintEventArgs e)
{
    Graphics g = e.Graphics;
    // 使用using自动释放画刷
    using (LinearGradientBrush brush = new LinearGradientBrush(
        new Rectangle(0, 0, 150, 80),
        Color.LightBlue,
        Color.DarkBlue,
        LinearGradientMode.Vertical))
    {
        g.FillRectangle(brush, 10, 10, 150, 80);
    }
    using (Pen pen = new Pen(Color.Black))
    {
        g.DrawRectangle(pen, 10, 10, 150, 80);
    }
    // 绘制文本
    using (Font font = new Font("微软雅黑", 12))
    {
        g.DrawString("GDI+示例", font, Brushes.White, 20, 40);
    }
}

2.1 坐标变换与平滑处理

如果希望旋转或缩放图形,可以使用g.TranslateTransformg.RotateTransform。此外设置g.SmoothingMode = SmoothingMode.AntiAlias能够抗锯齿,让边缘更平滑,但会轻微影响性能。

需要注意变换是累加的,若多次调用变换方法而未重置,图形位置可能偏离预期。可以在绘制前调用g.Save()保存状态,绘制后g.Restore()恢复,避免相互干扰。

三、自定义控件重绘与双缓冲

当我们需要在控件上持续更新图形,比如实时曲线或动画,仅依赖单次Paint不够。应当调用Invalidate()通知系统重绘,并在OnPaint中根据当前数据画图。频繁闪烁是典型问题,解决办法是开启双缓冲。

自定义控件中可以将DoubleBuffered设为true,或者设置SetStyle(ControlStyles.OptimizedDoubleBuffer, true)。以下代码展示一个最简自定义圆形进度控件:

public class CircleProgress : Control
{
    public int Value { get; set; } = 0;

    public CircleProgress()
    {
        this.DoubleBuffered = true;
        this.Size = new Size(120, 120);
    }

    protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        Graphics g = e.Graphics;
        g.SmoothingMode = SmoothingMode.AntiAlias;
        // 画底环
        using (Pen back = new Pen(Color.LightGray, 10))
        {
            g.DrawEllipse(back, 10, 10, 100, 100);
        }
        // 画进度弧
        using (Pen fore = new Pen(Color.Green, 10))
        {
            int angle = (int)(360.0 * Value / 100);
            g.DrawArc(fore, 10, 10, 100, 100, 0, angle);
        }
    }
}

3.1 重绘时机与性能权衡

调用Invalidate()不会立即重绘,而是将控件加入无效区域,下一次消息循环时触发Paint。若需立刻更新可接着调用Update()Refresh(),但后者会强制同步重绘,频繁使用易造成界面卡顿。

对于复杂场景,推荐把图形先画到Bitmap上,再在Paint中一次性DrawImage,这样既能利用双缓冲,又方便做图层管理。配合TaskTimer更新数据模型,再通知界面重绘,是较为稳健的架构。

四、常见误区与调试建议

一个广泛存在的误解是认为CreateGraphics得到的对象可以持久保存并随时画。实际上窗口重绘时系统会使用Paint事件里的Graphics覆盖之前内容,因此持久绘图必须放在Paint或缓存位图中。

调试GDI+问题可以借助g.VisibleClipBounds查看裁剪区,或临时输出坐标值。若图形不显示,优先检查控件尺寸是否为0、画笔颜色是否与背景相同、以及是否忘记调用Invalidate。掌握这些细节后,C# GDI+就能稳定支撑各类桌面图形需求。

GDI+CSharp_drawingGraphics修改时间:2026-08-09 20:30:30

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。