At boops boops suggestion, I'm posting my first thread on this forum.
If you want to use a control's methods, and update its image through methods and events of the control, that work has to be done on the thread the Control was created, which is usually the GUI thread.
But, if you don't need to use the methods of the control, and you want to handle all the rendering of the screen area of the control yourself and are planning on doing so at a regular, quick rate as in animating a game so that you don't need to respond to the paint event, Microsoft has provided the BufferedGraphics object to allow for this. The following was my post in a thread about updating from a non-GUI thread. I've just copied and pasted that here.
...
You can use a BufferedGraphics object in any thread and draw into its buffer, then render that to the screen from that thread.
Microsoft provided that as a way to bypass the Windows GUI event processing overhead for programmers who want to have complete control over drawing to an area of the screen. The paint event processing with doublebuffered set is actually relatively slow at refreshing the screen from the backbuffer, limiting the update to around 128 hz, I believe.
An issue is that you can't refresh part of the screen area from the buffer like you can with the paint event by invalidating a rectangle. Of course you could have multiple bufferedGraphic objects to handle different parts of the screen, say an out the window view, a dashboard, mirror view, menus, etc.
Of course you could still have other areas that the gui thread still handled, if you wanted to use controls.
It is designed as a backbuffer frame that you do all your drawing on, then render the whole buffer to the screen area of a graphics object.
It is really meant for something that will be updating the screen regularly, like a game, so you don't need paint events because you are updating the whole area rapidly by design.
If you update rapidly in the GUI thread using the paint event, you can click and hold on the form's titlebar and you will notice the GUI thread stops for a short period, probably waiting to see if you're going to double-click or something.
If you update the screen area from the background thread, it keeps on running, so you don't get that pause.
Using a BufferedGraphics object is one of the few times that using CreateGraphics to get a graphics object is the correct thing to do.
Normally when you use GUI control from the GUI thread, then you would not use CreateGraphics, you would use the Graphics Object passed to you in the paint event.
I did part of a space invaders game as an experiment, and the GUI thread portion of the code was very short, just responding to the key inputs to the game.
The game loop ran in a background thread and did all the drawing for the game from that thread. Another background thread was responsible for playing the sounds of the game.
There was no drawing done in the GUI thread at all.
For a speed test, not the way the game would normally be run, I couldn't get paint events any faster than 128 hz, so the normal GUI thread method of updating was pretty much limited to that top speed. But using the background thread, I could update the screen thousands of times per second. Again, you wouldn't want to do that, but it is nice to have the bandwidth to do some extra drawing knowing that the refresh from backbuffer to screen would take very little time.
Anyway, a simple example where a panel's Graphics object (which would draw on the screen area "above" the panel) is passed to a BufferedGraphics object, so it can render to that area of the screen directly, bypassing the need to update that area in the GUI thread.
The background thread will just generate 400 random rectangles in the buffer, then render it to the screen area of the panel.
To show that it is the panel's area of the screen being updated by the BufferedGraphics object, you can click and drag on the panel to move it around while the background thread is rendering to it.
Create a new project. Add a panel to it, and make it a reasonable size, to fill a good portion of the form, but not docked, so you can move it around.
Paste in the code and run. Also drag on the panel.
Code:
Public Class Form1
Dim thread1 As New Threading.Thread(AddressOf ThreadProc1)
Dim appIsActive As Boolean = True
Dim pnlHeight As Integer
Dim pnlWidth As Integer
Private Sub Form1_FormClosing(sender As Object, e As System.Windows.Forms.FormClosingEventArgs) Handles Me.FormClosing
appIsActive = False
Threading.Thread.Sleep(100) 'allow the thread to exit
End Sub
Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
pnlHeight = Panel1.ClientSize.Height
pnlWidth = Panel1.ClientSize.Width
thread1.IsBackground = True
thread1.Start()
End Sub
Private Sub ThreadProc1()
Dim g As Graphics = Panel1.CreateGraphics
Dim rect As New Rectangle(0, 0, 100, 100)
Dim sr As New Random
Dim c As Color
Dim br As New SolidBrush(Color.White)
Dim gog As BufferedGraphics
Dim goc As BufferedGraphicsContext = BufferedGraphicsManager.Current
gog = goc.Allocate(g, New Rectangle(0, 0, pnlWidth, pnlHeight))
Dim gb As Graphics = gog.Graphics
Do While appIsActive
For i As Integer = 0 To 399
rect.X = sr.Next(pnlWidth)
rect.Y = sr.Next(pnlHeight)
rect.Width = sr.Next(5, 100)
rect.Height = sr.Next(5, 100)
c = Color.FromArgb(sr.Next(256), sr.Next(256), sr.Next(256))
br.Color = c
gb.FillRectangle(br, rect)
Next
gog.Render()
Loop
g.Dispose()
br.Dispose()
End Sub
Private Sub Panel1_MouseMove(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles Panel1.MouseMove
Dim pnl As Panel = DirectCast(sender, Panel)
Static lp As Point
If e.Button = Windows.Forms.MouseButtons.Left Then
pnl.Left += e.X - lp.X
pnl.Top += e.Y - lp.Y
Else
lp = e.Location
End If
End Sub
End Class