Bart Verbakel 3 Posted August 19, 2023 Hello, I want to use bold text in specific listview cells, depending on the value in the cell, for example a bold font for all negative values. I can put bold text in a listview using the ListView1AdvancedCustomDrawSubItem event, but i that case I have to specify the row and column number in the eventcode. Since I do not know up front which values are negative, I cannot specify fixed row and column numbers in the code. I tried to solve it with a public variable 'BoldText', which is set to True if the text has to be bold. If value < 0 then BoldText:=True else Boldtext:=False; Listitem:=ListView1.Items.Add; ListItem.Subitems.Add(IntToStr(value)); .... procedure TForm8.ListView1AdvancedCustomDrawSubItem(Sender: TCustomListView; Item: TListItem; SubItem: Integer; State: TCustomDrawState; Stage: TCustomDrawStage; var DefaultDraw: Boolean); begin if BoldText then Sender.Canvas.Font.Style := [fsBold]; end; Unfortunately this does not work, all fonts are regular. However, when I use the code: procedure TForm8.ListView1AdvancedCustomDrawSubItem(Sender: TCustomListView; Item: TListItem; SubItem: Integer; State: TCustomDrawState; Stage: TCustomDrawStage; var DefaultDraw: Boolean); begin if (Item.Index=6) and (SubItem=8) then Sender.Canvas.Font.Style := [fsBold]; end; It works OK for cell (6,8) Does anybody have a solution? It looks like a simple problem, but I cannot figure out how to solve it. Thanks in advance, Bart Share this post Link to post
aehimself 396 Posted August 19, 2023 It's not working with a global variable because you cannot make sure that it has the right value the moment the ListItem (re)draws an item (e.g. during scrolling). Instead, you have to tie this data to each independent node. Declare a type, e.g.: TListItemData = Class public BoldText: Boolean; End; Then, when adding a list item: var li := ListItem1.Items.Add; li.Data := TListItemData.Create; (li.Data As TListItemData).BoldText := True; To make sure you are not leaking memory, add an OnDeletion handler to your ListView: If Assigned(Item) Then Begin TListItemData(Item.Data).Free; Item.Data := nil; End; After all this, in your custom draw methods you can check the object's property: If Assigned(Item.Data) And (Item.Data As TListItemData).BoldText Then Sender.Canvas.Font.Style := [fsBold] Else Sender.Canvas.Font.Style := []; I did not run this code so some minor adjustments might be needed but the basics are this. Also, instead of a custom class you simply can create and assign a PBoolean to Node.Data, but a class is more versatile if you want to add more properties later on. Share this post Link to post
Bart Verbakel 3 Posted August 19, 2023 Ok, thank you for your reply. I will try if I can get it working like this. Bart Share this post Link to post
Bart Verbakel 3 Posted August 19, 2023 This code doesn't seem to work (for me) var li := ListItem1.Items.Add; I think you meant: var li:=Listview1.items.Add? li.Data := TListItemData.Create; Constructor Create needs a parent. Listview1, Self of nil does not work as a parent. Share this post Link to post
aehimself 396 Posted August 19, 2023 Yes, it should be ListView1.Items.Add. If you declared your class like I posted (descending from “nothing” = TObject) it does not require an owner (not a parent). If the definition is correct, it’s still possible that the RTL has a component named TListItemData, so you can try to name yours “TMyListItemData” or something else - that will help the compiler to recognize which one you want to create and what parameters it requires. Share this post Link to post
Remy Lebeau 1393 Posted August 19, 2023 (edited) Rather than using a separate class in the TListItem.Data property, it would be safer and more efficient to derive a new class from TListItem and add whatever members you want to it, and then return that class type from the ListView's OnCreateItemClass event. You can then type-cast any TListItem in the ListView to your custom class type when needed, and you don't have to worry about freeing them manually. For example: type TMyListItem = class(TListItem) public BoldText: Boolean; end; procedure TForm8.ListView1CreateItemClass(Sender: TCustomListView; var ItemClass: TListItemClass); begin ItemClass := TMyListItem; end; procedure TForm8.ListView1AdvancedCustomDrawSubItem(Sender: TCustomListView; Item: TListItem; SubItem: Integer; State: TCustomDrawState; Stage: TCustomDrawStage; var DefaultDraw: Boolean); begin if TMyListItem(Item).BoldText then Sender.Canvas.Font.Style := [fsBold]; end; ... var li := ListView1.Items.Add; TMyListItem(li).BoldText := True; Of course, in this particular example, there is a much simpler solution - just look at the data being drawn and act accordingly, eg: procedure TForm8.ListView1AdvancedCustomDrawSubItem(Sender: TCustomListView; Item: TListItem; SubItem: Integer; State: TCustomDrawState; Stage: TCustomDrawStage; var DefaultDraw: Boolean); begin if (SubItem > 0) and (StrToInt(Item.SubItems[SubItem-1]) < 0) then Sender.Canvas.Font.Style := [fsBold]; end; Edited August 19, 2023 by Remy Lebeau 2 Share this post Link to post