Real Software Forums

The forum for Real Studio and other Real Software products.
[ REAL Software Website | Board Index ]
It is currently Wed Nov 22, 2017 12:40 am
xojo

All times are UTC - 5 hours




Post new topic Reply to topic  [ 5 posts ] 
Author Message
 Post subject: Multiple PictureBoxes to a single Canvas?
PostPosted: Sun Oct 28, 2012 8:34 pm 
Offline

Joined: Sat Aug 18, 2012 11:23 am
Posts: 21
Hello all,

I just purchased a hobbyist version of RB as I look at moving a VB2010 game over to RB.

In VB2010 I use multiple PictureBoxes to control how much of the screen has to be re-drawn at any given moment, it also gave me easier access to Mouse events.

For example:

XXXXXXXXXXXXXX <- A PictureBox that has 6 buttons I draw.

YYYYYYYYYYYYYY <- A PictureBox that has 24 image icons I display scaled as necessary

ZZZZZZZZZZZZZZ
Z Z
Z Z
Z Z
Z Z <- A PictureBox where the main drawing takes place.
Z Z
Z Z
Z Z
ZZZZZZZZZZZZZZ


Based on what I've read, in RB multiple canvases is not the way to go. I tried creating the screen above drawing each of the X, Y, and Z elements every time any event was raised. It takes longer than I'd like, and also seems to be a waste of resources since, for instance, maybe only Icon Image 13 is "depressed" by a mouse click, but the whole screen has to be redrawn.

I'm wondering if I'm missing something. I've seen posts here where it's been mentioned that a Canvas control can be used as a ToolTip. But I'm not sure how to bring it to the front.

Which makes me wonder if there's some use of .Visible = True/False that I should be using.

I also looked at the Clip graphics method but I couldn't figue out if it was possible to keep sections of the "clip" persistent as I redrew other "clips" of the screen.

Anyway, I apologize for rambling. Basically, I'm trying to break my old VB habits and join the RB lifestyle.

Can anyone point me toward a solution to my X, Y, Z example?

Is redrawing the whole screen the best way to go?

Is it possible to compartmentalize drawing without having to redraw the whole screen?

Thank you,
RBnoob


Top
 Profile  
Reply with quote  
 Post subject: Re: Multiple PictureBoxes to a single Canvas?
PostPosted: Sun Oct 28, 2012 9:42 pm 
Offline
User avatar

Joined: Mon Apr 02, 2007 2:08 am
Posts: 1225
Location: San Francisco, CA, USA
RBnoob2012 wrote:
Is redrawing the whole screen the best way to go?

This is how I usually do it, which isn't recommended for Mac OS X apps but under Windows it works well

Quote:
Is it possible to compartmentalize drawing without having to redraw the whole screen?

Thank you,
RBnoob


Drawing to the screen is expensive, but drawing someplace else is less so. If you have multiple calls to g.Draw* in your Canvas' Paint event, things can be very slow indeed (again, this is a RB on Windows perspective; Mac devs here have told me otherwise.)

What I do is create a Picture object and do all my drawing to it in the background. Then, when the Canvas' Paint event fires, just draw the Picture once.

So, for example this:
Sub Paint(g As Graphics)
g.DrawRect(10, 10, 15, 15)
g.FillOval(30, 30, 15, 15)
End Sub


becomes this:
Sub Paint(g As Graphics)
Dim Buffer As New Picture(g.Width, g.Height, 32)
Buffer.Graphics.DrawRect(10, 10, 15, 15)
Buffer.Graphics.FillOval(30, 30, 15, 15)
g.DrawPicture(Buffer, 0, 0)
End Sub


Making the Buffer a property of the window (or someplace else more persistent than the local scope) also allows you to compartmentalize the drawing since other methods and objects can draw to the buffer at any time, even if the Paint event hasn't been raised. This also means that the Paint event can be Raised without redrawing the buffer.

As an example, take a look at the DragCanvas class in this project: https://github.com/charonn0/RB-Custom-Controls

With a little work, the Canvas can do a lot: https://www.youtube.com/watch?v=8DqTvcPZuvkl

_________________
Boredom Software


Top
 Profile  
Reply with quote  
 Post subject: Re: Multiple PictureBoxes to a single Canvas?
PostPosted: Sun Oct 28, 2012 11:43 pm 
Offline

Joined: Sat Aug 18, 2012 11:23 am
Posts: 21
charonn0,

First, thanks for stopping by to help. I looked at your example, did you mean the DragContainter Class in your DragCanvas project? That's the one I looked at. Thanks for the example.

You said that redrawing the whole screen isn't recommended for Mac. Do you know what is?

In your example code on GitHub I noticed you used Refresh(false) and Invalidate(false). I've seen this a few other times, and I think it's the key to what I'm missing in terms of buffering and/or double-buffering.

So ... if I understand your example of "this becomes this" (in your reply post, not your github code) ... I would move all of my screen drawing routines (i.e. Sub RedrawTopSection, Sub RedrawMidSection, Sub RedrawMainSection) to the Paint event of my Canvas1 canvas, correct? And in each of these subs that draws a section of the screen, I would be using a global gBuffer so that each is drawing to the same memory block, right?

Then at the end I:
g.DrawPicture(gBuffer, 0, 0)
which puts the gBuffer image into Canvas1, correct?

I have been using Canvas1.BackDrop = gBuffer ... maybe that's why I'm getting flickering.

Okay ... I'm not sure I'm clear on how I force the Paint event. Assuming that my drawing subs have been moved to Paint ... when a user "clicks" an image icon, I capture the Mouse, determine which Icon it is, then redraw the screen with that Icon in its "Down" image, and then g.DrawPicture(). Is that right? The whole screen would be redrawn with the Clicked Icon in its Down position, and then the MouseUp capture redraws the whole thing again, with all icons in their "Up" position.

Am I on the right track? Would .Refresh(False) or .Invalidate(False) be better for the situation I described, or should I use g.DrawPicture() as you indicated?

Thanks again for your help. I'm really grateful.

Regards,
RBnoob


Top
 Profile  
Reply with quote  
 Post subject: Re: Multiple PictureBoxes to a single Canvas?
PostPosted: Mon Oct 29, 2012 12:23 am 
Offline
User avatar

Joined: Mon Apr 02, 2007 2:08 am
Posts: 1225
Location: San Francisco, CA, USA
Quote:
You said that redrawing the whole screen isn't recommended for Mac. Do you know what is?

From the highest authority: viewtopic.php?p=247297#p247297

RBnoob2012 wrote:
charonn0,
did you mean the DragContainter Class in your DragCanvas project

Well, both. The DragContainer class specifically and the DragCanvas project generally.

In the DragContainer.Paint event, I do this:

Sub Paint(g As Graphics)
If lastWidth <> Me.Width Or lastHeight <> Me.Height Then
//The canvas has been resized, so we need to regen the buffer
buffer = New Picture(Me.Width, Me.Height, 24)
lastWidth = Me.Width
lastHeight = Me.Height
Update()
End If
g.DrawPicture(buffer, 0, 0)
End Sub


The Update() method of the DragContainer example is where all the actual drawing gets done. If the Canvas hasn't been resized then the only thing the Paint event does is draw the unmodified buffer, otherwise it calls Update() and then draws the newly generated buffer. Other events or methods also call Update if they've modified anything (and they may or may not call Refresh/Invalidate to force a Paint.) You can certainly have a different method each draw different or even overlapping regions on the buffer, calling them as needed (and as little as possible from the Paint event.)

Drawing to the screen will always be expensive, and multiple Paint events can fire in a very short period of time. The core idea is that you want to get in and get out of the Paint event as quickly as possible since there may be 10 more coming quick.

Quote:
when a user "clicks" an image icon, I capture the Mouse, determine which Icon it is, then redraw the screen with that Icon in its "Down" image, and then g.DrawPicture(). Is that right? The whole screen would be redrawn with the Clicked Icon in its Down position, and then the MouseUp capture redraws the whole thing again, with all icons in their "Up" position.


Yes. The buffer is the current state of the canvas, which you modify as needed/wanted. When the Paint event fires you just call g.DrawPicture(Buffer, 0, 0). Since the buffer persists until it's explicitly destroyed, you don't necessarily need to redraw everything, but you would need to devise a way of determining what needs to be redrawn and what doesn't. The DragContainer example just redraws everything when Update is called.

Quote:
In your example code on GitHub I noticed you used Refresh(false) and Invalidate(false).


Refresh triggers the Paint event, Invalidate merely suggests a Paint event. Passing True to either one will erase the canvas first, whereas passing false will not. Erasing the background is a case-by-case thing, since it can be good but it's also more expensive.

_________________
Boredom Software


Top
 Profile  
Reply with quote  
 Post subject: Re: Multiple PictureBoxes to a single Canvas?
PostPosted: Mon Oct 29, 2012 6:16 pm 
Offline

Joined: Sat Aug 18, 2012 11:23 am
Posts: 21
charonn0,

Got it working. Thanks so much, charonn0. I'm grateful.

Warm Regards,
RBnoob


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 5 posts ] 

All times are UTC - 5 hours


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
Jump to:  
cron
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group