WinForms 中的事件绘制

问题描述 投票:0回答:1

我有一个根据以下代码片段绘制分形的方法:

 public static void DrawFractal(int x, int y, int len, double angle, PaintEventArgs e,Panel panel1)
        {
            Graphics g = e.Graphics;
            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);
            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, e,panel1);
                DrawFractal((int)x1, (int)y1, (int)(len / 1.5), angle - 15, e,panel1);
            }

        }
private void panel1_Paint(object sender, PaintEventArgs e)
        {
            FractalTree.DrawFractal(panel1.Width / 2, panel1.Height / 2, 80, 0, e, panel1);
        }

现在这个方法在窗口打开时进行绘制。我希望它在单击按钮时绘制。

private void button2_Click(object sender, EventArgs e)
{
}

我还想添加稍后停止渲染的功能。因此,如果您能提出使用与渲染相关的处理程序和事件的想法,我将很高兴。

[这就是它的样子][1]:https://i.sstatic.net/OGcC0.png [2]:https://i.sstatic.net/mQeHc.png

c# .net winforms
1个回答
4
投票

这里遇到的主要问题是 CPU 密集型操作。在 UI 线程中执行这种冗长的递归例程会冻结它,直到执行完成。因此,正如您所提到的,单击

Stop
按钮不会执行任何操作。

为了保持 UI 响应能力,请创建方法的

Task
版本以在工作线程中运行它们,以便可以等待它们完成或取消。我这里有两个例子可以建议。

绘制位图

Bitmap
上绘制形状,然后在
Paint
事件中绘制它。

public class FractalTree
{
    public static async Task<Bitmap> DrawFractalAsync(
        Rectangle canvas, double len, double angle,
        Color backColor, Color treeColor,
        CancellationToken token)
    {
        var bmp = new Bitmap(canvas.Width, canvas.Height);

        using (var g = Graphics.FromImage(bmp))
        using (var pn = new Pen(treeColor))
        {
            g.Clear(backColor);

            await Task.Run(async () => await DrawLocal(
                canvas.Width / 2, canvas.Top, len, angle), token);

            Task DrawLocal(double nx, double ny, double newLen, double newAngle)
            {
                if (token.IsCancellationRequested)
                    token.ThrowIfCancellationRequested();

                double x1 = nx + newLen * Math.Sin(newAngle * Math.PI * 2 / 360.0);
                double y1 = ny + newLen * Math.Cos(newAngle * Math.PI * 2 / 360.0);

                g.DrawLine(pn,
                    (float)nx, canvas.Height - (float)ny,
                    (float)x1, canvas.Height - (float)y1);

                if (newLen > 2)
                {
                    DrawLocal(x1, y1, newLen / 1.5, newAngle + 30);
                    DrawLocal(x1, y1, newLen / 1.5, newAngle - 15);
                    //DrawLocal(x1, y1, newLen / 1.5, newAngle + 15);
                    //DrawLocal(x1, y1, newLen / 1.5, newAngle - 15);
                }

                return Task.CompletedTask;
            }
        }

        return bmp;
    }
}

实现示例...

public partial class YourForm : Form
{
    private Bitmap bmp;
    private CancellationTokenSource cts;

    public YourForm()
    {
        InitializeComponent();

        // Input controls...
        cmbTemplates.SelectedIndexChanged += (s, e) => Draw();
        nudLen.ValueChanged += (s, e) => Draw();
        nudAngle.ValueChanged += (s, e) => Draw();
        nudOffsetX.ValueChanged += (s, e) => Draw();
        nudOffsetY.ValueChanged += (s, e) => Draw();
    }

    protected override void OnFormClosed(FormClosedEventArgs e)
    {
        base.OnFormClosed(e);
        bmp?.Dispose();
        cts?.Dispose();
    }

    private void pnlCanvas_Paint(object sender, PaintEventArgs e)
    {
        if (bmp != null)
            e.Graphics.DrawImage(bmp, Point.Empty);
    }

    private void btnDraw_Click(object sender, EventArgs e) => Draw();

    private void btnCancel_Click(object sender, EventArgs e)
    {
        cts?.Cancel();
    }

    private async void Draw()
    {
        if (cts != null) return;
        btnDraw.Enabled = false;
        var canvas = pnlCanvas.ClientRectangle;
        Bitmap newBmp = null;

        try
        {
            using (var cancelTS = new CancellationTokenSource())
            {
                cancelTS.Token.ThrowIfCancellationRequested();
                cts = cancelTS;

                if (tcMain.SelectedTab == tpFractal)
                {
                    switch (cmbTemplates.SelectedIndex)
                    {
                        case 0:
                            var len = (int)nudLen.Value;
                            var angle = (double)nudAngle.Value;
                            var xo = (int)nudOffsetX.Value;
                            var yo = (int)nudOffsetY.Value;
                            
                            canvas.Offset(xo, yo);

                            newBmp = await FractalTree
                                .DrawFractalAsync(
                                canvas, len, angle, 
                                pnlCanvas.BackColor,
                                Color.Back, cts.Token);
                            break;
                        case 1:
                            // Call another FractalTree algorithm...
                            break;
                    }
                }
                else if (tcMain.SelectedTab == tpPyramid)
                {
                    // Pyramid algorithms 
                }
            }
             bmp?.Dispose();
             bmp = newBmp;
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("Canceld...!");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            btnDraw.Enabled = true;
            pnlCanvas.Invalidate();
            cts = null;
        }
    }
}

演示

SO73893159A

绘制形状

使任务计算并返回定义形状的结构列表,并将它们传递给相关的

Graphics.Draw...
Graphics.Fill...
方法。

public class FractalTree
{
    public static async Task<IEnumerable<PointF[]>> GetFractalPointsAsync(
        Rectangle camvas, double len, double angle, CancellationToken token)
    {
        var points = new ConcurrentBag<PointF[]>();

        await Task.Run(async () => await Generate(
            camvas.Width / 2, 1, len, angle), token);

        Task Generate(double x, double y, double newLen, double newAngle)
        {
            double nx = x + newLen * Math.Sin(newAngle * Math.PI * 2 / 360.0);
            double ny = y + newLen * Math.Cos(newAngle * Math.PI * 2 / 360.0);
            var pt1 = new PointF((float)x, camvas.Height - (float)y);
            var pt2 = new PointF((float)nx, camvas.Height - (float)ny);

            points.Add(new[] { pt1, pt2 });

            if (newLen > 2)
            {
                Generate(nx, ny, newLen / 1.5, newAngle + 30);
                Generate(nx, ny, newLen / 1.5, newAngle - 15);
                //GenerateLocal(nx, ny, newLen / 1.5, newAngle + 15);
                //GenerateLocal(nx, ny, newLen / 1.5, newAngle - 15);
            }

            return Task.CompletedTask;
        }

        return points;
    }
}

...并编辑实现示例如下...

public partial class YourForm : Form
{
    private CancellationTokenSource cts;
    private IList<PointF[]> treePoints;

    public YourForm()
    {
        InitializeComponent();

        cmbTemplates.SelectedIndexChanged += (s, e) => Draw();
        nudLen.ValueChanged += (s, e) => Draw();
        nudAngle.ValueChanged += (s, e) => Draw();
        nudOffsetX.ValueChanged += (s, e) => Draw();
        nudOffsetY.ValueChanged += (s, e) => Draw();
    }

    protected override void OnFormClosed(FormClosedEventArgs e)
    {
        base.OnFormClosed(e);
        cts?.Dispose();
    }

    private void pnlCanvas_Paint(object sender, PaintEventArgs e)
    {
        if (treePoints != null)
        {
            foreach (var pts in treePoints)
                e.Graphics.DrawCurve(Pens.Black, pts);
        }
    }

    private void btnDraw_Click(object sender, EventArgs e) => Draw();

    private void btnCancel_Click(object sender, EventArgs e)
    {
        cts?.Cancel();
    }

    private async void Draw()
    {
        if (cts != null) return;
        btnDraw.Enabled = false;
        var canvas = pnlCanvas.ClientRectangle;

        try
        {
            using (var cancelTS = new CancellationTokenSource())
            {
                cts = cancelTS;

                if (tcMain.SelectedTab == tpFractal)
                {
                    switch (cmbTemplates.SelectedIndex)
                    {
                        case 0:
                            var len = (int)nudLen.Value;
                            var angle = (double)nudAngle.Value;
                            var xo = (int)nudOffsetX.Value;
                            var yo = (int)nudOffsetY.Value;

                            canvas.Offset(xo, yo);

                            var points = await FractalTree
                                .GetFractalPointsAsync(
                                canvas, len, angle, cts.Token);

                            treePoints = points.ToList();
                            break;
                        case 1:
                            // Call another FractalTree algorithm...
                            break;
                    }
                }
                else if (tcMain.SelectedTab == tpPyramid)
                {
                    // Pyramid algorithms 
                }
            }
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("Canceld...!");
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            btnDraw.Enabled = true;
            pnlCanvas.Invalidate();
            cts = null;
        }
    }
}

这会产生一些漂亮而简单的动画:

SO73893159B

© www.soinside.com 2019 - 2024. All rights reserved.