You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#如何跨方法访问Graphics类在非Paint方法中实现屏幕绘制

原代码错误原因

你现在的写法有两个核心问题,导致绘图无法生效:

  • 赋值逻辑完全写反:Paint事件里的e.Graphics是系统提供的、绑定当前控件绘图表面的有效对象,你把自己定义的、从未赋值过有效实例的静态变量g赋值给e.Graphics,相当于把本来可用的绘图对象替换成了空值,完全拿不到正确的绘图上下文。
  • Graphics对象是和单次绘制周期绑定的临时资源:就算你反过来写g = e.Graphics把系统的绘图对象缓存成全局变量,等本次Paint事件执行结束,系统就会释放这个对象,后续再调用缓存的g执行绘图操作,要么无效要么直接抛出对象已释放的异常,根本无法正常绘制。
可行实现方案

不要长期缓存Graphics对象,GDI+绘图的标准逻辑是:所有持久化的绘制内容都应该在Paint事件触发时,使用系统传入的e.Graphics完成绘制;非Paint触发的场景下,先更新绘图状态,再调用控件的Invalidate()方法主动触发重绘即可。

方案1:传参调用+统一重绘(推荐)

这是最稳定、符合官方规范的写法,绘制的内容会持久保留,不会因为窗体重绘消失:

// 用于存储绘图状态的变量,需要画线时设为true即可
private bool _shouldDrawLine = false;

private void paintClass(object sender, PaintEventArgs e)
{
    // 正确逻辑:拿到系统提供的有效Graphics,传给绘图方法
    if (_shouldDrawLine)
    {
        DrawLine(e.Graphics);
    }
}

// 绘图方法接收Graphics作为参数,不持有全局Graphics对象
public void DrawLine(Graphics g)
{
    // GDI+资源如Pen、Brush用完要释放,用using包裹自动释放最稳妥
    using (Pen linePen = new Pen(Color.AliceBlue))
    {
        g.DrawLine(linePen, 10, 10, 20, 20);
    }
}

// 你可以在任意其他方法里触发画线,不需要直接操作Graphics
public void TriggerDrawLine()
{
    _shouldDrawLine = true;
    // 通知控件重绘,会自动触发paintClass逻辑完成绘制
    this.Invalidate();
}

如果要画的内容比较多,可以把所有要绘制的图形、坐标、颜色都存在对应的集合/状态变量里,Paint触发时遍历所有状态一次性绘制即可。

方案2:临时创建Graphics(仅适合临时即时绘制场景)

如果你不需要绘制内容持久保留(比如拖拽过程中的临时预览线),可以通过控件的CreateGraphics方法临时获取绘图上下文,用完立刻释放:

public void DrawLine()
{
    // 以当前窗体为例,要在哪个控件上画就调用哪个控件的CreateGraphics
    using (Graphics g = this.CreateGraphics())
    using (Pen linePen = new Pen(Color.AliceBlue))
    {
        g.DrawLine(linePen, 10, 10, 20, 20);
    }
}

注意:该方案绘制的内容是临时的,只要控件发生重绘(比如被其他窗口遮挡、窗口最小化还原、大小调整),之前画的内容就会被擦除,正式业务场景不推荐用这个方案。

内容的提问来源于stack exchange,提问作者msnba

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 23:57:25