WinForms中按钮触发分形绘制及终止渲染方案咨询
WinForms分形绘制:切换触发方式与添加终止功能
一、改为Button点击触发绘制
原来的分形会在窗口加载、面板重绘时自动绘制,改成button2点击触发的步骤如下:
移除自动绘制逻辑
删掉原panel1_Paint事件里的分形调用代码,或者直接注释掉事件内的执行逻辑,避免自动触发。绑定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
相关产品推荐
相关产品推荐

