Draw on a form by a separate thread

10,222

I think you will need to draw to a Bitmap, then in the OnPaint Method, draw that bitmap to the window. I will demonstrate in a moment.

As Hans pointed out, in the OnPaint method you are setting

formGraphics = e.Graphics;

but at the end of the method e.Graphics is disposed, so you can't use it anymore, if your code got to

Engine.Draw(formGraphics);

you would get an exception.

So basically you need to have a global

Bitmap buffer = new Bitmap(this.Width, this.Height)

in your asynced thread you would invoke your drawing to that Bitmap you can use

Graphics g=Graphics.FromBitmap(buffer);//

To get a graphics object, but remember you have to

g.Dispose()

it or wrap it in a

using (Graphics g=Graphics.FromBitmap(buffer))
{
//do something here

}

I am going to play with it for a few moments and see if I can get you a working sample

EDIT Here's your working sample. I started a new form and tossed a button on it. I changed the mainform backgroundimagelayout to none.

I think you need to be using .net 4.0 or better, if not using this, let me know I can change it to match your version... I think.

//you need this line to use the tasks
using System.Threading.Tasks;

namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    public void Draw()
    {
        Bitmap buffer;
        buffer = new Bitmap(this.Width, this.Height);
        //start an async task
        Task.Factory.StartNew( () =>
        {
                using (Graphics g =Graphics.FromImage(buffer))
                {
                    g.DrawRectangle(Pens.Red, 0, 0, 200, 400);
                    //do your drawing routines here
                }
            //invoke an action against the main thread to draw the buffer to the background image of the main form.
            this.Invoke( new Action(() =>
            {
                    this.BackgroundImage = buffer;
            }));
        });

    }

    private void button1_Click(object sender, EventArgs e)
    {
        //clicking this button starts the async draw method
        Draw();
    }

}

}

Share:
10,222
Alaa Arori
Author by

Alaa Arori

Updated on July 22, 2022

Comments

  • Alaa Arori
    Alaa Arori almost 2 years

    I'm trying to build a multithreaded game where I have a separate thread for painting on the form which is not the main thread. this brings us to thread-safe technics which I've read many articls about, but I'm not really sure I got it correctly.

    my problem is that I have a structure where every data object is painting it self on the form so I didn't figure out how to implement it.

    this is a snippet of my working mono-thread code:

    public partial class Form1 : Form
    {
        GameEngine Engine;
        public Form1()
        {
            InitializeComponent();
            Engine = new GameEngine();
        }
        protected override void OnPaint(PaintEventArgs e)
        {
            Engine.Draw(e.Graphics);
        }
    

    }

    class GameEngine
    {
    
        Maze Map;
        List<Player> Players;
    
        public void Draw(Graphics graphics)
        {
                Map.Draw(graphics);
                foreach (var p in Players)
                {
                    p.Draw(graphics);
                }
        }
    

    }

    so please can anyone give me a hint or a link to good article helping me to learn how to separate the drawing on an another thread?.

    [Edit]

    I managed to implement what I intended to do and this is how I coded it

        protected override void OnPaint(PaintEventArgs e)
        {
            formGraphics = e.Graphics;
            DisplayThread = new Thread(new ThreadStart(Draw));
            DisplayThread.Start();
        }
    
        private void Draw()
        {
            if (this.InvokeRequired)
            {
                this.Invoke(new DrawDelegate(this.Draw));
            }
            else
            {
                Engine.Draw(formGraphics);
            }
        }
    

    but I got an ArgumentException : Parameter is not valid

    would you please point to the error in that code