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

Graphics.FillEllipse()执行Task.Delay()后抛出参数无效异常求助

Graphics.FillEllipse()执行Task.Delay()后抛出参数无效异常求助

嗨,我来帮你捋清楚这个问题的根源和解决办法~

首先,你遇到的System.ArgumentException异常,核心原因是**Paint事件里的Graphics对象生命周期和异步等待的冲突**:

  • Paint事件触发时,系统会临时创建一个Graphics实例(也就是e.Graphics)给你用来绘制界面,一旦Paint事件处理方法执行完毕,系统就会自动回收这个Graphics对象,它就不再有效了。
  • 你在方法里用了await Task.Delay(wait),这会让方法暂停执行,控制权交还给UI线程,Paint事件会提前结束——这时候e.Graphics已经被系统释放了。等Delay结束后,你再调用FillEllipse去操作这个已经失效的Graphics对象,自然就会抛出“参数无效”的异常。

另外,async void在UI事件里使用本身就有风险,一旦异步过程中出现异常,会直接触发未捕获异常,可能导致程序崩溃,所以尽量别在Paint这类UI事件里用异步等待。

给你两个可行的解决方案:

方案一:用Timer控制绘制时机(推荐)

把异步等待的逻辑移出Paint事件,改用System.Windows.Forms.Timer来定时触发重绘,Paint事件只负责同步绘制当前状态的内容:

  1. 先提前把所有星星数据加载到内存里(避免绘制时频繁读文件拖慢UI),然后用Timer每隔指定时间更新当前要绘制的帧,再触发重绘。

示例代码大概是这样:

// 定义成员变量存储数据和状态
private int currentFrame = 0;
private Timer _drawTimer;
private List<(double x, double z)> _allStarData; // 存储所有星星的坐标数据
private readonly int _totalFrames; // 总帧数

public Form1()
{
    InitializeComponent();
    // 提前加载所有星星数据
    LoadAllStarData();
    // 计算总帧数
    _totalFrames = seconds * tickspersecond - 1;
    // 初始化Timer
    _drawTimer = new Timer
    {
        Interval = wait, // 你的等待毫秒数
        Enabled = false
    };
    _drawTimer.Tick += OnDrawTimerTick;
    // 可以在Form加载完成后启动Timer
    this.Load += (s, e) => _drawTimer.Start();
}

private void LoadAllStarData()
{
    _allStarData = new List<(double x, double z)>();
    // 一次性读取所有坐标数据,提前乘上constant
    while (!readerx.EndOfStream && !readerz.EndOfStream)
    {
        double x = Convert.ToDouble(readerx.ReadLine()) * constant;
        double z = Convert.ToDouble(readerz.ReadLine()) * constant;
        _allStarData.Add((x, z));
    }
    // 释放文件读取器(现在可以放心释放了)
    readerx.Dispose();
    readerz.Dispose();
}

private void OnDrawTimerTick(object sender, EventArgs e)
{
    if (currentFrame < _totalFrames)
    {
        currentFrame++;
        Invalidate(); // 触发Paint事件重绘当前帧
    }
    else
    {
        _drawTimer.Stop(); // 绘制完成,停止Timer
    }
}

// 修改后的Paint事件,只负责同步绘制
private void Form1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.Clear(Color.White);
    // 计算当前帧要绘制的星星范围
    int startIdx = currentFrame * starslenght;
    if (startIdx + starslenght > _allStarData.Count)
        return;
    // 绘制当前帧的所有星星
    for (int i = startIdx; i < startIdx + starslenght; i++)
    {
        var star = _allStarData[i];
        e.Graphics.FillEllipse(brush, (int)star.x + 960, (int)star.z + 540, 2, 2);
    }
}

方案二:使用异步任务更新状态,触发重绘

如果必须保留异步读取的逻辑(比如数据量太大没法一次性加载),可以把文件读取放到异步任务里,每次读取一帧的数据后,调用Invalidate()触发重绘,而不是在Paint事件里等待:

private int _currentFrame = 0;
private List<(double x, double z)> _currentFrameStars;

private async void StartDrawing()
{
    try
    {
        for (_currentFrame = 0; _currentFrame < seconds * tickspersecond - 1; _currentFrame++)
        {
            // 异步读取当前帧的星星数据
            _currentFrameStars = new List<(double x, double z)>();
            for (int i = 0; i < starslenght; i++)
            {
                double x = Convert.ToDouble(await readerx.ReadLineAsync()) * constant;
                double z = Convert.ToDouble(await readerz.ReadLineAsync()) * constant;
                _currentFrameStars.Add((x, z));
            }
            Invalidate(); // 触发重绘
            await Task.Delay(wait);
        }
        // 最后释放资源
        readerx.Dispose();
        readerz.Dispose();
    }
    catch (Exception ex)
    {
        // 处理异常,避免程序崩溃
        MessageBox.Show($"绘制出错:{ex.Message}");
    }
}

private void Form1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.Clear(Color.White);
    if (_currentFrameStars == null)
        return;
    // 同步绘制当前帧的星星
    foreach (var star in _currentFrameStars)
    {
        e.Graphics.FillEllipse(brush, (int)star.x + 960, (int)star.z + 540, 2, 2);
    }
}

额外注意点:

  • 不要复用Paint事件的Graphics对象,每次绘制都要用当前事件提供的e.Graphics。
  • 文件读取操作尽量不要在UI线程执行,方案二里用了ReadLineAsync来异步读取,避免阻塞UI。
  • 记得在合适的时机释放brush、文件读取器等资源,避免内存泄漏。

备注:内容来源于stack exchange,提问作者levvalentin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.21 11:53:12