canvas custom listbox scroll recommendation

I am almost done creating a very simple canvas based listbox. I need to be able to scroll the “rows”. I know how to scroll an image in a canvas per the documentation (scrolling a picture in the canvas). But if my CanvasListbox has a bunch of rows, the picture would be really tall, i.e. row height of 20 pixels and 200 rows would have a height of 4000 pixels. Seems too big. Does anyone have suggestions how to go about this without creating a super tall picture to scroll?

If n rows fit in the canvas then you need to draw only n+1 rows when scrolling.
Just the starting index changes when a row has fully entered the canvas.

Markus is right. Only draw the rows that you can see. Calculate the index of the first row by dividing the scroll offset by your row height (off the top of my head, I think you’ll need to round up). The y offset of that first row for drawing will be min((controlHeight - scrollOffset),0) mod rowHeight. Then figure out how many rows are visible. If your row height is static, you can just divide. If the row height is variable, you could just keep track of where the bottom of the last row was and stop when you reach g.Height.

Out of interest. Would this code be equal to what you’re suggesting ? Greg.

for i as integer = ubound(me.rows)
row.top = nextTop
row.left = 0 
row.width = sourcelist.width
row.height = sourcelist.rowheight

if row.top > sourcelist.height then
exit
end if

nextTop = nextTop + row.height
next

Thanks for the suggestions guys here’s what I have so far in the paint event of the canvas:

[code] //class properties:
//DefaultRowHeight as integer = 20
//rowtext() as String <-- as a test in the open event I appended 50 “test rows” (“Test1”, “Test2” etc…)
//scrollposition as integer
//scrollrequired as Boolean

dim p as new Picture(me.Width, me.Height)

for j as integer = ScrollPosition to UBound(RowText())
dim rectwidth as integer = me.Width
dim rectheight as integer = DefaultRowHeight

dim textbox as new REALbasic.Rect(0, (DefaultRowHeight*(j-ScrollPosition)), rectwidth, rectheight)

if textbox.Bottom >= (me.Height)  then
  ScrollRequired = True
  exit for j
  
else
  ScrollRequired = False
end if

p.Graphics.DrawRect(textbox.Left,textbox.Top,textbox.Width,textbox.Height)
p.Graphics.DrawString(rowtext(j),textbox.Left + 5,textbox.top + (textbox.Height/2) + (p.Graphics.textAscent/2))

next

g.DrawPicture(p,0,0)
[/code]

I have ScrollRequired as boolean in there because I don’t want to scroll out of bounds on RowText(). But haven’t figured that out yet.

Put the definitions for rectwidth and rectheight outside of the loop as they don’t change value based on the loop.

The rest of it looks okay (without going to my computer and trying it).

Greg: I guess when you embed ContainerControls within a canvas, then that is what the canvas does? Only draws the visible ones?

It may, but everything you’ve embedded does stay in memory. Embedding is not what this user is doing though… he’s drawing each row every time, and if there are a lot of rows, he certainly could exceed the max height of a picture at 32767 (and there’s really no reason to actually draw the text on each of those hidden rows every time).

Thinking about the way iOS does this (and they certainly do it for the sake of memory and speed), they only allocate enough rows to fill what’s available on screen and then they reuse the rows that fall off one end to request and draw the ones that appear on the other.

It might not be what he is doing now, but my question is aimed at: why not?

He probably has an array of textboxes and draws them to a canvas so they stay in memory anyway.

Might he not just as well use embedWithin and leave all the drawing to the canvas?

Is there any good reason to do it yoursel like “it is much faster”?

I don’t have any control arrays or control sets or a container control. I have a canvas and an array of rects and an array of coinciding strings. The array of rects keeps track of where I can click rows and of course the array of strings is the text that is printed on each row. The only good reason for me is having more control of how it’s drawn. I doubt it will be faster than the built in listbox.

[quote=87554:@Markus Winter]It might not be what he is doing now, but my question is aimed at: why not?

He probably has an array of textboxes and draws them to a canvas so they stay in memory anyway.

Might he not just as well use embedWithin and leave all the drawing to the canvas?

Is there any good reason to do it yoursel like “it is much faster”?[/quote]

Well, like I said, my main concern is that the backing picture could end up being taller than 32767px, and then you’d need to rework the control because that’s the max height for a picture. Larger pictures = more memory! and they’re not compressed in memory, so depending on the width of the control! you could eat up memory pretty fast.

I wrote a Teleprompter app a few years ago (like they use on TV) and had this exact problem because the XP machine it was running on had only 512MB of RAM. After a while, either the pic would get too big, or we’d run out of memory. To keep the scroll running smoothly, we moved to this dynamic loading method.

…and yes, I think this approach would ultimately be MUCH faster if there were a lot of rows. Instead of having the entire loading hit up front, it’d be dispersed over the usage of the control. Any rows that the user never even needed to see wouldn’t even get drawn.

We might talk about different things here.

I’m asking: if you would simply use EmbedWithin then the canvas would stay small anyway, wouldn’t it? As I understand it it simply keeps a list of items added and draws them as required …

I checked the DownloadContainer example and neither the canvas height nor g.height changes when I follow adding additional containerControls in the debugger …

Correct, but the original poster is creating a picture, and drawing that onto the canvas. THAT is what I’m talking about.

Not me (as suspected) :wink:

Just thought I’d point out a potentially better way as he doesn’t have to deal with the logistics of what needs to be drawn himself but can leave it to the canvas :wink:

I actually need those logistics. The standard text box won’t let me draw things the way I want. To simplify my example code, I trimmed out a lot of the special drawing that I need (drop shadows, beveled text etc. )

Would love to see a screenshot of how it looks (I’m nosy) …

I’ll post something up once I get it closer to how I want it to look. :slight_smile: