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

WinForms中按钮触发分形绘制及终止渲染方案咨询

WinForms分形绘制:切换触发方式与添加终止功能

一、改为Button点击触发绘制

原来的分形会在窗口加载、面板重绘时自动绘制,改成button2点击触发的步骤如下:

  1. 移除自动绘制逻辑
    删掉原panel1_Paint事件里的分形调用代码,或者直接注释掉事件内的执行逻辑,避免自动触发。

  2. 绑定button2点击事件并实现触发逻辑
    在窗体设计器中给button2添加Click事件,用标记变量控制绘制状态,再触发面板重绘:

    // 窗体类全局变量,标记是否需要绘制分形
    private bool _drawFractalFlag = false;
    
    private void button2_Click(object sender, EventArgs e)
    {
        _drawFractalFlag = true;
        // 触发面板重绘,会调用panel1_Paint事件
        panel1.Invalidate();
    }
    
    // 保留panel1_Paint事件,仅在标记为需要绘制时执行分形绘制
    private void panel1_Paint(object sender, PaintEventArgs e)
    {
        if (_drawFractalFlag)
        {
            FractalTree.DrawFractal(panel1.Width / 2, panel1.Height / 2, 80, 0, e, panel1);
        }
    }
    

二、添加终止渲染功能

原分形是同步递归绘制,会阻塞UI线程,必须改成异步绘制+终止令牌的方式实现终止功能:

1. 新增全局控制变量

在窗体类中添加以下变量:

// 用于发送终止信号
private CancellationTokenSource _cancelTokenSource;
// 标记是否正在绘制
private bool _isDrawingInProgress = false;

2. 修改button2点击事件,启动异步绘制

private async void button2_Click(object sender, EventArgs e)
{
    if (_isDrawingInProgress) return; // 防止重复点击触发多次绘制

    // 重置终止令牌
    _cancelTokenSource?.Cancel();
    _cancelTokenSource = new CancellationTokenSource();
    _isDrawingInProgress = true;
    _drawFractalFlag = true;

    // 异步执行绘制,避免阻塞UI线程
    await Task.Run(() =>
    {
        using (Graphics g = panel1.CreateGraphics())
        {
            // 先清空面板
            g.Clear(panel1.BackColor);
            // 调用修改后的带终止检查的分形绘制方法
            FractalTree.DrawFractal(panel1.Width / 2, panel1.Height / 2, 80, 0, g, panel1, _cancelTokenSource.Token);
        }
    });

    _isDrawingInProgress = false;
}

3. 修改分形绘制方法,加入终止检查

修改原静态DrawFractal方法,新增CancellationToken参数,在递归的每一步检查是否需要终止:

public static void DrawFractal(int x, int y, int len, double angle, Graphics g, Panel panel1, CancellationToken cancelToken)
{
    // 收到终止信号就立即退出递归
    if (cancelToken.IsCancellationRequested)
        return;

    double x1, y1;
    x1 = x + len * Math.Sin(angle * Math.PI * 2 / 360.0);
    y1 = y + len * Math.Cos(angle * Math.PI * 2 / 360.0);
    
    // 跨线程操作控件必须用Invoke确保在UI线程执行
    panel1.Invoke(new Action(() =>
    {
        g.DrawLine(new Pen(Color.Black), x, panel1.Height - y, (int)x1, panel1.Height - (int)y1);
    }));

    if (len > 2)
    {
        DrawFractal((int)x1, (int)y1, (int)(len / 1.5), angle + 30, g, panel1, cancelToken);
        DrawFractal((int)x1, (int)y1, (int)(len / 1.5), angle - 15, g, panel1, cancelToken);
    }
}

4. 实现终止按钮(比如button3)的点击事件

添加一个终止按钮(命名为button3),绑定点击事件实现终止逻辑:

private void button3_Click(object sender, EventArgs e)
{
    // 发送终止信号
    _cancelTokenSource?.Cancel();
    // 更新状态标记
    _isDrawingInProgress = false;
    _drawFractalFlag = false;
    // 清空面板
    panel1.Invalidate();
}

核心注意事项

  • UI线程安全:异步绘制时,所有操作控件的代码必须通过Invoke切换到UI线程执行,否则会抛出跨线程访问异常。
  • 重复触发防护:通过_isDrawingInProgress标记避免多次点击绘制按钮导致的并发问题。
  • 资源清理:CancellationTokenSource使用后要及时处理,避免内存泄漏,这里每次启动绘制前都会重置旧的令牌源。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 10:01:18