Home > database >  Event Paint in WinFrom
Event Paint in WinFrom

Time:10-01

I have a method which drawing Fractals

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

Now this method draws when the window is opened. I would like it to be drawn on button click.

private void button2_Click(object sender, EventArgs e)
        {

        }

I would also like to add the ability to stop rendering later. Therefore, I would be very happy if you could suggest the idea of ​​working with handlers and events related to rendering.

[This is how it will look like][1]: https://i.stack.imgur.com/OGcC0.png [2]: https://i.stack.imgur.com/mQeHc.png

CodePudding user response:

The main issue that you have here is the cpu-bound operations. Executing this kind of lengthy-recursive routines in the UI thread freezes it until the completion of the execution. Hence clicking the Stop button - as you mentioned - does nothing.

To keep the UI responsive, create Task versions of your methods to run them in worker threads so they can be awaited to complete or canceled. I have here two examples to suggest.

Draw Bitmap

Draw the shapes on a Bitmap then draw it in the Paint event.

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

Implementation example...

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;
        }
    }
}

Demo

SO73893159A

Draw the Shape

Make the tasks calculate and return lists of structures that define the shapes and pass them to the relevant Graphics.Draw... or Graphics.Fill... methods.

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

... and edit the implementation example as follows...

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;
        }
    }
}

Which produces some nice and simple animation:

SO73893159B

  • Related