Coloring individual ListBox rows

Hello,

I have a category listbox, and a button captioned “Color”.
I want to let the user to be able to click on any individual row inside the listbox, then click color, then choose a color, and voila, the color for that row change.

Inside the BUTTON’s Action event:

[code] Call SelectColor(Globals.CategoryColor, “Select a color”)
Globals.ToBeColored = lbCategory.ListIndex

Globals.ColoredCategories.Append(Globals.ToBeColored)[/code]

Inside LISTBOX’s CellTextPaint event:

FOR EACH index AS Integer in Globals.ColoredCategories() g.ForeColor = Globals.CategoryColor NEXT

But all my code does is change the color for ALL OF THE ROWs.

I want to for example color a row as blue, another as green, and some other rows as red.

Please help.

In the CellTextPaint event, you need to find ROW in your array of index values and then find the corresponding color for that row. Which means you need to save the colors in addition to the indexes.

Button Action

Call SelectColor(Globals.CategoryColor, "Select a color")
Globals.ToBeColored = lbCategory.ListIndex
  
Globals.ColoredCategories.Append(Globals.ToBeColored)
Globals.ColoredCategoryColors.Append(Globals.CategoryColor)

Listbox CellTextPaint

dim n as integer
n = Globals.ColoredCategories.IndexOf(Row)
if n>= 0 then g.ForeColor = Globals.ColoredCategoryColors(n)

Perfect,
Thanks

[quote=77704:@Tim Hare]
Listbox CellTextPaint

dim n as integer n = Globals.ColoredCategories.IndexOf(Row) if n>= 0 then g.ForeColor = Globals.ColoredCategoryColors(n) [/quote]

I just have problem understanding this part.
I don’t quite get how the CellTextPaint event works.
Is it an all-time-running-behind-the-scenes type of event? meaning it keeps repeating itself for the whole life of the opened control?
And how does it understand to which row I want to assign which color?

Yes, every time the OS and/or the Xojo framework thinks (or rather: knows) a redraw is necessary it calls that event.
In addition you can initiate the redrawing by calling Invalidate() or InvalidateCell(row, column) on the listbox from somewhere else in the listbox or the window and it will be redrawn.

Look at the signature of CellTextPaint, there is a row and a column parameter telling you which cell it is:

Function CellTextPaint(g as Graphics, Row as Integer, Column as Integer, x as Integer, y as Integer) As Boolean

When the listbox needs to be redrawn, CellBackgroundPaint will be called once for each visible cell.

dim n as integer
n = Globals.ColoredCategories.IndexOf(Row)
if n>= 0 then g.ForeColor = Globals.ColoredCategoryColors(n)

So the Row parameter passed to IndexOf refers to the currently selected cell’s row?

No, it refers to the row of the cell that is currently being drawn. Has nothing to do with being selected or not. Say you have a listbox with 2 columns and 4 visible rows. Xojo is periodically going to do the equivalent of

CellBackGroundPaint(0, 0)
CellBackGroundPaint(0, 1)
CellBackGroundPaint(1, 0)
CellBackGroundPaint(1, 1)
CellBackGroundPaint(2, 0)
CellBackGroundPaint(2, 1)
CellBackGroundPaint(3, 0)
CellBackGroundPaint(3, 1)

Ie., it will call CellBackgroundPaint 8 times in succession, changing the Row and Column parameters each time.

Thank you so much, now I almost fully understood how CellTextPaint and CellBackgroundPaint events work.
I just wonder what does RETURN TRUE/FALSE does inside these events.

Returning True means you have completely handled the drawing and the framework won’t draw over what you did.

Tim Hare’s code had some bugs (namely the inability to REPLACE the color of an already colored category)
I debugged Button Action code, CellTextPaint doesn’t contain any bugs. it now works perfectly, here’s the final code:

Button Action

[code] Call SelectColor(Globals.CategoryColor, “Select a color”)
Globals.ToBeColored = lbCategory.ListIndex

IF Globals.ColoredCategories.IndexOf(lbCategory.ListIndex) >= 0 THEN
// IF CATEGORY WAS ALREADY COLORED IN THE PAST >>> REPLACE COLOR
dim ToBeReplaced as integer
ToBeReplaced = Globals.ColoredCategories.IndexOf(lbCategory.ListIndex)
Globals.ColoredCategoryColors.Remove(ToBeReplaced)
Globals.ColoredCategoryColors.Insert ToBeReplaced, globals.CategoryColor
ELSE
// IF CATEGORY DOESN’T HAVE ANY COLOR YET
Globals.ColoredCategories.Append(Globals.ToBeColored)
Globals.ColoredCategoryColors.Append(Globals.CategoryColor)
END IF[/code]

Thank you Tim, you expanded my skills.

I do probably oversee something. I place te scores of children in a listbox, use Celltag, Celltextpaint and that works. I can place e. g. a red field with blue text or whatever in every cell I want. I want to vary with color, textcolor and text.

But when i do place e. g. a blue field with yellow text, all the cells where I placed something before, turn to the last selected color and text. The text remains :wink: How do I keep the right colors in the cell and prevent that they will be overwritten? It is written programmaticaly but it is always between two buttonpresses. (Return), so the cells are filled after a buttonpress, at least it looks that way.

I am quite stuck with it. I found Listbox1.InvalidateCell(row , column) but it does not do anything. How can I preserve the color and text I had and prevent that not all my cells get the color and textcolor of the last input?
Some code would be helpful :wink:

Sincerely,

Tom

Double posted, I am sorry.

You may want to create an array of colors for TextColor with as many rows and columns as the Lisbox, and another for BackgroundColor, so then you can set each cell differently.

I do not quite understand where and how to. This is in my celltexpaint event, do you mean that?

Me.CellTag(RT, CT) = Kleur REM color in English

Select Case RT
case RT
Select Case CT
case CT
Select Case Me.CellTag(RT, CT)

  Case "Red"
    g.ForeColor = &cff0000
    g.FillRect(0, 0, g.Width-1, g.Height-1)
    
    
  Case "Green"
    g.ForeColor = &c00ff00
    g.FillRect(0, 0, g.Width-1, g.Height-1)
    
  Case "Blue"
    g.ForeColor = &c0000ff
    g.FillRect(0, 0, g.Width-1, g.Height-1)
    
    
  Case "Black"
    g.ForeColor = &c000000
    g.FillRect(0, 0, g.Width-1, g.Height-1)
    
    
  Case "Yellow"
    g.ForeColor = &cffff00
    g.FillRect(0, 0, g.Width-1, g.Height-1)
    
  Case "White"
    g.ForeColor = &cffffff
    g.FillRect(0, 0, g.Width-1, g.Height-1)
    
    
  Case "Tekst"
    g.ForeColor = &cff0000
    me.Cell(RT,CT) = Celtekst
    
  Else
    
    
  End Select
  
Else
  
End Select

Else

End Select

Me.CellTag(RT, CT) = TekstKleur REM Textcolor
I use English and Dutch names to prevent to get confused :wink:

Select Case Tekstkleur
Case “Zwart”
g.ForeColor = &c000000
Case “Wit”
g.ForeColor = &cffffff
Case “Rood”
g.ForeColor = &cff0000
Case “Groen”
g.ForeColor = &c00ff00
Case “Blauw”
g.ForeColor = &c0000ff
Case “Geel”
g.ForeColor = &cffff00

Else
g.ForeColor = &c000000
'g.ForeColor = &cffffff
end Select

That would work too.

I works perfecty in the sense that I get the right combination in the cell, but that is not the point, I want to preserve the color, textcolor and text for every individual cell and place other ones in other cells. I fail on that.

Don’t put it in the CELLTEXTPAINT event, put it in the CELLBACKGROUNDPAINT event

When it is that simple ;-). I ll give it a try, get an outofbounds now at Me.CellTag(RT, CT) = Kleur, never had that :wink:

Why is it btw? I mean why in cellbackgroundpaint?

Remember… CELL Paint events get called for every VISIBLE cell… NOT just for the ones you have data in
This is why you might get “out of bounds”. Look at ListCount property to insure you stay “in bounds”