.net WinForms中的事件绘制

wko9yo5t  于 2022-12-20  发布在  .NET
关注(0)|答案(1)|浏览(95)

我有一个方法,绘制分形每以下片段:

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)
{
}

我还想添加以后停止渲染的功能。因此,如果您能建议 使用与呈现相关的处理程序和事件。
[This is how it will look like][1]: https://i.stack.imgur.com/OGcC0.png [2]: https://i.stack.imgur.com/mQeHc.png

jecbmhm3

jecbmhm31#

这里的主要问题是CPU绑定操作。在UI线程中执行这种冗长的递归例程会冻结它,直到执行完成。因此,单击Stop按钮-正如您提到的-什么也不会做。
要保持UI的响应性,请创建方法的Task版本,以便在工作线程中运行它们,这样就可以等待它们完成或取消。

绘制位图

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

public class FractalTree
{
    public static void DrawFractal(
        Graphics g, Rectangle canvas, int x, int y, int len, double angle)
    {
        double x1 = x + len * Math.Sin(angle * Math.PI * 2 / 360.0);
        double y1 = y + len * Math.Cos(angle * Math.PI * 2 / 360.0);

        g.DrawLine(Pens.Black, x, canvas.Height - y, (int)x1, canvas.Height - (int)y1);

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

    public static async Task DrawFractalAsync(
        Graphics g, Rectangle canvas, 
        int x, int y, int len, double angle,
        CancellationToken token)
    {
        await Task.Run(async () =>
        {
            double x1 = x + len * Math.Sin(angle * Math.PI * 2 / 360.0);
            double y1 = y + len * Math.Cos(angle * Math.PI * 2 / 360.0);

            g.DrawLine(Pens.Black, x, canvas.Height - y, (int)x1, canvas.Height - (int)y1);

            if (len > 2)
            {
                await DrawFractalAsync(g, canvas, 
                    (int)x1, (int)y1, (int)(len / 1.5), angle + 30, token);
                await DrawFractalAsync(g, canvas, 
                    (int)x1, (int)y1, (int)(len / 1.5), angle - 15, token);
                //await DrawFractalAsync(g, canvas, 
                    //(int)x1, (int)y1, (int)(len / 1.5), angle + 15, token);
                //await DrawFractalAsync(g, canvas, 
                    //(int)x1, (int)y1, (int)(len / 1.5), angle - 15, token);
            }
        }, token);
    }
}

实现示例...

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?.Cancel();
        cts?.Dispose();
    }

    private void pnlCanvas_Paint(object sender, PaintEventArgs e)
    {
        if (bmp != null)
        {
            var r = pnlCanvas.ClientRectangle;

            e.Graphics.DrawImage(bmp, r, 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel);
        }
    }

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

    private void btnCancel_Click(object sender, EventArgs e)
    {
        cts?.Cancel();
        bmp?.Dispose();
        bmp = null;
        pnlCanvas.Invalidate();
    }

    private async void Draw()
    {
        if (!btnDraw.Enabled) return;
        btnDraw.Enabled = false;

        var canvas = pnlCanvas.ClientRectangle;

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

                if (tcMain.SelectedTab == tpFractal)
                {
                    bmp?.Dispose();
                    bmp = null;
                    bmp = new Bitmap(canvas.Width, canvas.Height);

                    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;

                            using (var g = Graphics.FromImage(bmp))
                                await FractalTree.DrawFractalAsync(g, canvas, 
                                    xo + canvas.Width / 2, yo + canvas.Height / 2,
                                    len, angle, cts.Token);
                            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;
        }
    }
}

演示

画出形状

让任务计算并返回定义形状的结构列表,并将它们传递给相关的Graphics.Draw...Graphics.Fill...方法。

public class FractalTree
{
    public static void GetFractalPoints(Rectangle camvas, 
        int x, int y, int len, double angle, List<List<Point>> lines)
    {
        double x1 = x + len * Math.Sin(angle * Math.PI * 2 / 360.0);
        double y1 = y + len * Math.Cos(angle * Math.PI * 2 / 360.0);

        var pt1 = new Point(x, camvas.Height - y);
        var pt2 = new Point((int)x1, camvas.Height - (int)y1);

        lines.Add(new List<Point> { pt1, pt2 });

        if (len > 2)
        {
            GetFractalPoints(camvas, (int)x1, (int)y1, (int)(len / 1.5), angle + 30, lines);
            GetFractalPoints(camvas, (int)x1, (int)y1, (int)(len / 1.5), angle - 15, lines);
            //GetFractalPoints(camvas, (int)x1, (int)y1, (int)(len / 1.5), angle + 15, lines);
            //GetFractalPoints(camvas, (int)x1, (int)y1, (int)(len / 1.5), angle - 15, lines);
        }
    }

    public static async Task GetFractalPointsAsync(
        Rectangle camvas, int x, int y, int len, double angle, 
        List<List<Point>> lines,
        CancellationToken token)
    {
        await Task.Run(async () =>
        {
            double x1 = x + len * Math.Sin(angle * Math.PI * 2 / 360.0);
            double y1 = y + len * Math.Cos(angle * Math.PI * 2 / 360.0);

            var pt1 = new Point(x, camvas.Height - y);
            var pt2 = new Point((int)x1, camvas.Height - (int)y1);

            lines.Add(new List<Point> { pt1, pt2 });

            if (len > 2)
            {
                await GetFractalPointsAsync(camvas, (int)x1, (int)y1, 
                    (int)(len / 1.5), angle + 30, lines, token);
                await GetFractalPointsAsync(camvas, (int)x1, (int)y1, 
                    (int)(len / 1.5), angle - 15, lines, token);
                await GetFractalPointsAsync(camvas, (int)x1, (int)y1, 
                    (int)(len / 1.5), angle + 15, lines, token);
                //await GetFractalPointsAsync(camvas, (int)x1, (int)y1, 
                //    (int)(len / 1.5), angle - 15, lines, token);
            }
        }, token);
    }
}

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

public partial class YourForm : Form
{
    private CancellationTokenSource cts;
    private readonly List<List<Point>> lines = new List<List<Point>>();

    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?.Cancel();
        cts?.Dispose();
    }

    private void pnlCanvas_Paint(object sender, PaintEventArgs e)
    {
        foreach (var line in lines.Where(l => l.Count == 2).ToList())
            e.Graphics.DrawCurve(Pens.Black, line.ToArray());
    }

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

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

    private async void Draw()
    {
        if (!btnDraw.Enabled) return;
        btnDraw.Enabled = false;
        lines.Clear();

        var canvas = pnlCanvas.ClientRectangle;

        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;

                            await FractalTree.GetFractalPointsAsync(canvas, 
                                xo + canvas.Width / 2, yo + canvas.Height / 2, 
                                len, angle, lines, cts.Token);
                            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;
        }
    }
}

它会生成一些漂亮而简单的动画:

相关问题