Delphi TListview OwnerDraw SubItems - change default font (it's bold somehow after you Draw on the canvas)

StackOverflow https://stackoverflow.com/questions/13485761

  •  01-12-2021
  •  | 
  •  

Question

If you use ownerdraw with a TListView, the subitems are all BOLD font style by default somehow, even if the listview font.style is set to [], for all the SubItems following a custom drawn one.

A workaround I found is forcing the Style set in the CustomDrawSubItem event:

ListView2.Canvas.Font.Style := [fsItalic];
ListView2.Canvas.Font.Style := [];

(a simple call with [] won't work unless the default style is set to something other than [], because the SetStyle call doesn't think the style has changed)

This is however an ugly fix which involves extra processing time. Is there a better solution?

Demo project: http://www.mediafire.com/?v8bsdpvpfqy47vn

Was it helpful?

Solution

I haven't encountered the exact situation you describe, but I have encountered a similar issue. When I use an owner-drawn TListView with an OnAdvancedCustomDrawSubItem event assigned to change the Canvas.Font on a per-subitem basis, I find that after I have changed the Sender.Canvas.Font for one subitem, subsequent subitems get drawn with the wrong settings even if I change the Sender.Canvas.Font for them. My workaround is to manually call the Sender.Canvas.Font.OnChange event handler at the end of my OnAdvancedCustomDrawSubItem event handler. That signals TListView to report back CDRF_NEWFONT to Windows, then everything gets drawn correctly. It is as if the Sender.Canvas.Font.OnChange event is not hooked up correctly while TListView is being owner-drawn, so it does not detect font changes and thus does not report back to Windows correctly.

OTHER TIPS

I agree with jachguate's comment with that there seems to be an issue with the VCL control; a possibe design issue with TCustomListView.CNNotify. But it's not easy to follow the logic there.

One solution is to force a change on the canvas of the control when DefaultDraw is true, so that the VCL creates and selects the control's font again to the passed DC before the custom drawing notification returns. Example:

procedure TForm1.LVCustomDrawSubItem(Sender: TCustomListView; Item: TListItem;
  SubItem: Integer; State: TCustomDrawState; var DefaultDraw: Boolean);
Var R: TRect;
    bmp: TBitmap;
    x: Integer;
begin
  DefaultDraw := True;

  if SubItem = 1 then begin
    DefaultDraw := False;
    ...
      ...       
      Sender.Canvas.Draw(R.Left - 2, R.Top, Bmp);
      Bmp.Free;
    end;
  end;

  if DefaultDraw then
    Sender.Canvas.Brush.Color := ColorToRGB(clWindow);     // <--
end;


The way I would prefer is to avoid using the control's canvas, if at all possible. You can use a temporary DC for your case, this also avoids the black background problem mentioned in the comments to the question.

uses
  commctrl;

  ...

procedure TForm1.LVCustomDrawSubItem(Sender: TCustomListView; Item: TListItem;
  SubItem: Integer; State: TCustomDrawState; var DefaultDraw: Boolean);
Var R: TRect;
    bmp: TBitmap;
    x: Integer;
    DC: HDC;
begin
  DefaultDraw := True;

  if SubItem = 1 then begin
    DefaultDraw := False;
    ...
      ...

      DC := GetDC(Sender.Handle);
      ImageList_Draw(TypeImages.Handle, 0, DC, R.Left - 2, R.Top, ILD_NORMAL);
      ReleaseDC(Sender.Handle, DC);

      Bmp.Free;
    end;
  end;
end;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top