How do you draw on a picturebox outside of the pain event.
Basically Im converting my old VB6 chip8 emulator into Vs2010 version, so far everything works except the drawing routine.
In Vb6 it was easy, simply
pict1.line(x1,y1)-(x2,y2),vbwhite, BF
etc..
I could write to the picture box directly within a different sub called from main() and also call the form refresh etc.
In Vb2010 it seems entirely different, you create a graphics object.
Dim g as graphics
Then assign it to an object with the create graphics method
g=picturebox1.creategraphics
then use g.drawline etc.. <<< this normally goes in the paint event of the object.
The problem im having is that nothing displays until the paint event, which is not how my code works.
Basically the emulator procedure framework is:-
LoadRom
Initialise stuff
Emulate
-Fetch opcodes
-Interpret
-Set VxtoVy
-Set ReturnFromSub
-DrawRoutine
As I try to draw on the picture box from within the DrawRoutine sub nothing displays, is there something else needed to cause a draw event to happen?
Re: How do you draw on a picturebox outside of the pain event.
Force it. Me.Invalidate in your emulation loop will force a redraw.
Painting on a picturebox is an ugly way to do things these days. If you need to do such things, it's easier to just paint on the form itself.
Re: How do you draw on a picturebox outside of the pain event.
Thanks for the quick reply, Ive inserted me.invalidate into the main interpret procedure but when im debugging and stepping through the code all i see is a form with blank grey box, does this need a doevents call. Im trying to see if the first pixels are displaying at debug time but nothing shows except the grey blank form etc..
Re: How do you draw on a picturebox outside of the pain event.
Quote:
Originally Posted by
Jenner
Painting on a picturebox is an ugly way to do things these days. If you need to do such things, it's easier to just paint on the form itself.
That's not necessarily true. A PictureBox is optimised for GDI+ and can provide smoother transitions.
The general principle of GDI+ is very simple:
1. You store the data that describes the drawing in member variables.
2. You handle the Paint event, read the data from those member variables and use the Graphics object provided to perform the drawing.
3. Whenever you want to change the drawing you update the member variables and then call Invalidate and Update or Refresh on the control.
That's all there is to it, besides the detail of exactly what Graphics methods to call. If you want an example then follow the CodeBank link in my signature and check out my Simple Drawing thread. You'll see there that, each time the user draws a line, the data representing that line is added to a member variable containing all the lines, Invalidate is called to specify what area of the control has changed and Update is called to raise the Paint event. The actual drawing is then done in the Paint event handler. That same principle should be employed no matter what you're drawing.
Re: How do you draw on a picturebox outside of the pain event.
Quote:
Originally Posted by
jmcilhinney
That's not necessarily true. A PictureBox is optimised for GDI+ and can provide smoother transitions.
Really? I always heard it was slower because it was going through another layer of objects.
Re: How do you draw on a picturebox outside of the pain event.
Quote:
Originally Posted by
jmcilhinney
That's not necessarily true. A PictureBox is optimised for GDI+ and can provide smoother transitions.
The general principle of GDI+ is very simple:
1. You store the data that describes the drawing in member variables.
2. You handle the Paint event, read the data from those member variables and use the Graphics object provided to perform the drawing.
3. Whenever you want to change the drawing you update the member variables and then call Invalidate and Update or Refresh on the control.
That's all there is to it, besides the detail of exactly what Graphics methods to call. If you want an example then follow the CodeBank link in my signature and check out my Simple Drawing thread. You'll see there that, each time the user draws a line, the data representing that line is added to a member variable containing all the lines, Invalidate is called to specify what area of the control has changed and Update is called to raise the Paint event. The actual drawing is then done in the Paint event handler. That same principle should be employed no matter what you're drawing.
Can you please clarify what you mean by invoking the paint event, heres an example sub which is called from main,
Code:
Sub DrawGfx()
Dim G As Graphics
G = Me.CreateGraphics()
G.FillRectangle(Brushes.Black, 10, 10, 200, 300)
Randomize()
Dim x, y, w, h
G = Me.CreateGraphics()
G.Clear(Color.Black)
x = CInt(Math.Floor((200 - 10 + 1) * Rnd())) + 10
y = CInt(Math.Floor((300 - 10 + 1) * Rnd())) + 10
w = CInt(Math.Floor((100 - 10 + 1) * Rnd())) + 10
h = CInt(Math.Floor((200 - 10 + 1) * Rnd())) + 10
G.FillRectangle(Brushes.White, x, y, w, h)
Me.Invalidate()
Me.Refresh()
Label1.Text = Now()
Me.Show()
End Sub
When i debug this the rectangle never shows, just a blank form, but if i press a button and event click then this drawing methods works ok, im not sure why. So drawing to the form only seems to work from a event being triggered not by invoking it within a subroutine.
this works ok:-
Code:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim G As Graphics
G = Me.CreateGraphics()
G.FillRectangle(Brushes.Black, 10, 10, 200, 300)
End Sub
how do I synthesize an event so I can draw to the picturebox, because the emulation is determined by the drawing opcode not an event triggered by a button or object etc..
Hope I've made this clear.
Re: How do you draw on a picturebox outside of the pain event.
Drawing on a Graphics object, then calling Refresh/Invalidate, will never work. Unlike VB6 (AutoRedraw) .NET Windows Forms don't re-draw the graphics. You need to handle the Paint event or override OnPaint().
Re: How do you draw on a picturebox outside of the pain event.
try this:
vb Code:
dim img as bitmap
Sub DrawGfx()
img = new bitmap(picturebox1.width,picturebox1.height)
Dim G As Graphics = graphics.fromimage(img)
G.FillRectangle(Brushes.Black, 10, 10, 200, 300)'??? overdrawn in 3 lines from here
Randomize()
Dim x, y, w, h
G.Clear(Color.Black)
x = CInt(Math.Floor((200 - 10 + 1) * Rnd())) + 10
y = CInt(Math.Floor((300 - 10 + 1) * Rnd())) + 10
w = CInt(Math.Floor((100 - 10 + 1) * Rnd())) + 10
h = CInt(Math.Floor((200 - 10 + 1) * Rnd())) + 10
G.FillRectangle(Brushes.White, x, y, w, h)
picturebox1.image = img
Label1.Text = Now()
'Me.Show()???
End Sub
Re: How do you draw on a picturebox outside of the pain event.
Have you read my CodeBank thread yet? I created specifically so that we wouldn't have to keep creating the same examples over and over.
Re: How do you draw on a picturebox outside of the pain event.
You should just re-draw everything in the Paint event, basically. Why would you even want to draw anything outside of the Paint event?
P.S. @.paul.
1. CInt(Math.Floor(x)) is the same as CInt(x).
2. You should probably use the Random object instead.
3. You should dispose the Graphics object
4. You should give a type to x, y, w, and h.
But I guess it's just an example anyways.
Re: How do you draw on a picturebox outside of the pain event.
Quote:
Originally Posted by
minitech
CInt(Math.Floor(x)) is the same as CInt(x)
Nope.
Code:
MessageBox.Show(CInt(Math.Floor(1.5)).ToString())
MessageBox.Show(CInt(1.5).ToString())
CInt rounds, so if you want to truncate then CInt alone won't do.
Re: How do you draw on a picturebox outside of the pain event.
Quote:
Originally Posted by
minitech
You should just re-draw everything in the Paint event, basically. Why would you even want to draw anything outside of the Paint event?
Thanks for your tips, this was a really crude example though and im still quite new to .net but vb6 im proficient in, basically the reason I need to paint out of the paint event is because the Chip8 emulator interprets a drawing opcode called DxyN, when this opcode is processed the picturebox needs an Xor drawn to it, so what ever was there is overwritten, and then a certain regiter called reg(&hf) is set to 1 or 0 depending on the Xor, the problem with waiting for the paint event is that the chip8 interprets several more opcodes while waiting for the damn paint event to occur which results in a time delay which throws the whole timing of the drawing process out of sync, in fact the only way to paint directly is from a button event or some similar.
Quote:
Have you read my CodeBank thread yet? I created specifically so that we wouldn't have to keep creating the same examples over and over.
I have read your document thread, but as stated this only shows you have created objects that are triggered by an event (button click etc...) like trying to paint a bmp to the picturebox still will not work until the main form has finished it main procedure.
To clarify what I mean ive included a template of the emulator:
Code:
Public Class Form1
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Init()
Emulate()
End Sub
Sub Init()
End Sub
Sub Emulate()
FetchOps()
Interpret()
End Sub
Sub FetchOps()
'MsgBox("FetchOps")
End Sub
Sub Interpret()
'MsgBox("Interpret")
DrawGfx()
DoSomething()
End Sub
Sub DoSomething()
For i = 1 To 1000
For p = 1 To 100
Next p
Next i
End Sub
Sub DrawGfx()
Dim G As Graphics
Dim buffer As Bitmap
Dim MyBrush As SolidBrush
buffer = New Bitmap(PictureBox1.Width, PictureBox1.Height)
G = Graphics.FromImage(buffer)
MyBrush = New SolidBrush(Color.Red)
G.FillEllipse(MyBrush, 0, 0, PictureBox1.Width, PictureBox1.Height)
PictureBox1.Image = buffer
End Sub
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
End Sub
End Class
As you can see the paint event is never invoked until after the DoSomething() procedure has finished, this is what i mean by timing problems, because until the main() has finished the paint event never happens. So the DoSomething could be a long loop and cause considerable delay until the drawing actually happens.
Re: How do you draw on a picturebox outside of the pain event.
It's already been mentioned several times but apparently not clearly enough. Just add PictureBox1.Refresh at the end of your DrawGfx sub. Or put it in your button click sub, if that's where you want to trigger repainting.
The point is that Refresh causes instant repainting (of the whole control). So it will happen before your DoSomething routine starts.
Invalidate alone means "put the Paint task in a queue". Repainting takes place once the process is idle, so in your case it would happen after DoSomething.
Invalidate followed by Update has a similar effect to Refresh, but it gives you more flexibility. You can invalidate only the rectangles that need to be changed (and there might be dozens of them, e.g. in a game with animated sprites). The system optimizes drawing so that all invalidated areas of the control are combined, and the affected pixels are redrawn only once. That happens when you perform Update, or when the process has nothing better to do, whichever comes first. If you Refresh each sprite individually, on the other hand, it could slow everything down drastically: changing actual pixels on the screen is much slower than changing bits in memory.
hope this helps, BB
Re: How do you draw on a picturebox outside of the pain event.
Quote:
CInt rounds, so if you want to truncate then CInt alone won't do.
Now I know why my integer square root function wasn't working! Thanks! (Rep+ after a while)
Re: How do you draw on a picturebox outside of the pain event.
You're determined to do what you want to so you're not actually reading what's being posted:
Quote:
Originally Posted by jmcilhinney
Update is called to raise the Paint event
We are telling you, ABSOLUTELY, that you do NOT draw outside the Paint event handler or OnPaint method. Accept that and do as we say or keep fighting it and you'll never achieve your aim.
Re: How do you draw on a picturebox outside of the pain event.
Quote:
Originally Posted by
jmcilhinney
You're determined to do what you want to so you're not actually reading what's being posted:We are telling you, ABSOLUTELY, that you do NOT draw outside the Paint event handler or OnPaint method. Accept that and do as we say or keep fighting it and you'll never achieve your aim.
But jmc, there is no objection is there to defining a new bitmap, using a derived Graphics to draw on it, and then displaying the result in a PictureBox -- see post #12? Of course it should be done correctly (tinmanjo, please dispose of the Graphics after use!). If I'm not mistaken, the PictureBox.Image gets painted only after the Paint Event fires, so the same considerations about Invalidate/Refresh/Update apply.
BB
Re: How do you draw on a picturebox outside of the pain event.
Quote:
Originally Posted by
boops boops
But jmc, there is no objection is there to defining a new bitmap, using a derived Graphics to draw on it, and then displaying the result in a PictureBox -- see post #12? Of course it should be done correctly (tinmanjo, please dispose of the Graphics after use!). If I'm not mistaken, the PictureBox.Image gets painted only after the Paint Event fires, so the same considerations about Invalidate/Refresh/Update apply.
BB
No, that's fine, but drawing on an Image and drawing directly on a control are two different things. An Image is not a UI element and has no events. You can draw on an Image whenever you like.
The main difference between drawing on an Image and drawing on a control is that drawing on an Image is permanent, so you can't change it once it's done. Because controls get repainted over and over, changing what you've drawn on a control is a simple matter of waiting until the next Paint event, which may mean forcing one yourself, and then doing the new drawing.