Jump to content
Bart Verbakel

Using bold text in Listview

Recommended Posts

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

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

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

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

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 by Remy Lebeau
  • Like 2

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×