使用 VCL 样式时,ListView 行选择在图标周围被截断

ListView row selection gets cut off around Icon when using VCL styles

我使用以下代码在“CustomDrawSubItem”事件中从 PNG ImageList 的 ListView 子项上绘制图标。当我 select 一行或从“CustomDrawItem”更改行的画笔颜色时,此 selection 颜色将从子项的单元格中删除。我该如何解决这个问题,让“背景”颜色填充透明区域?

DPR 文件

program Project1;

uses
  Vcl.Forms,
  Unit1 in 'Unit1.pas' {Form1},
  Vcl.Themes,
  Vcl.Styles;

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  TStyleManager.TrySetStyle('Glow');
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

Unit1

unit Unit1;

interface

uses
   Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
   Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.ImageList, Vcl.ImgList,
   Vcl.ComCtrls, Winapi.CommCtrl, PngImageList;

type
  TForm1 = class(TForm)
    ListView1: TListView;
    PngImageList1: TPngImageList;
    procedure ListView1CustomDrawSubItem(Sender: TCustomListView; Item: TListItem; SubItem: Integer; State: TCustomDrawState; var DefaultDraw: Boolean);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure DrawPicOnListViewSubItem(LV: TListView; Item: TListItem; SubItem: LongInt; ImgListHandle: THandle; IconIndex,ImgListWidth: Word); inline;
Var R: TRect;
    x: LongInt;
begin
  R := Item.DisplayRect(drBounds);

  for x := 0 To SubItem - 1 Do
   R.Left := R.Left + LV.Columns[x].Width;

  R.Top := R.Top + 3;
  If Item <> nil then begin
    R.Left  := R.Left + (LV.Columns[SubItem].Width - ImgListWidth) div 2;
    R.Right := R.Left + ImgListWidth;
    // Ensure that the items are drawn transparently
    SetBkMode(LV.Canvas.Handle, TRANSPARENT);
    ListView_SetTextBkColor(LV.Handle, CLR_NONE);
    ListView_SetBKColor(LV.Handle, CLR_NONE);
    ImageList_Draw(ImgListHandle, IconIndex, LV.Canvas.Handle, R.Left - 2, R.Top, ILD_NORMAL);
  end;
end;

procedure TForm1.ListView1CustomDrawSubItem(Sender: TCustomListView; Item: TListItem; SubItem: Integer; State: TCustomDrawState; var DefaultDraw: Boolean);
begin
  if SubItem = 1 then begin
    DrawPicOnListViewSubItem(ListView1, Item, SubItem, PngImageList1.Handle, 0, 16);
    DefaultDraw := False;
  end;
end;

end.

Form1 DFM 文件

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 565
  ClientWidth = 954
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object ListView1: TListView
    Left = 0
    Top = 0
    Width = 954
    Height = 565
    Align = alClient
    Columns = <
      item
        Width = 200
      end
      item
      end
      item
        Width = 200
      end
      item
      end
      item
        Width = 200
      end>
    Items.ItemData = {
      052F0000000100000000000000FFFFFFFFFFFFFFFF03000000FFFFFFFF000000
      0000001890633600B8351F3D0000391F3DFFFFFFFFFFFF}
    RowSelect = True
    SmallImages = PngImageList1
    TabOrder = 0
    ViewStyle = vsReport
    OnCustomDrawSubItem = ListView1CustomDrawSubItem
  end
  object PngImageList1: TPngImageList
    PngImages = <
      item
        Background = clWindow
        Name = 'cross'
        PngImage.Data = {
          89504E470D0A1A0A0000000D49484452000000100000001008060000001FF3FF
          610000001974455874536F6674776172650041646F626520496D616765526561
          647971C9653C000001CB4944415478DAAD933F4842411CC7BF8719D890363455
          342946E349D1D09F2DA7245C8AA2A1A11A1A9C8CA0A684701197201A45A8A6A0
          E951434343448A43E8524BA20922096543F6ECF5BB7BA73CCCCD1E1CC7BDDFF7
          FBB97BBFEF3B661806BA79D8BF004E1903A3850DF0D314A1B1D700345D897A3A
          D4C4B6CBE4B502A4606967879F45A369212480A600B2B6180AF18B785CD608A0
          B5004926F6476A251CE6B95C0E1E8F07E7B19814AA4344E6D7D779B95C86CBE5
          C24D32296ABED526206102FC448D04363779269381D7EBC555222184980C0478
          A95482D3E9C4E3F5759A99606DCD0A10DFA42BC85C30C89F9F9EA4A156ABA158
          28C0E170E0239F9766FA244D6CF907F04DA3A120236E377FAB54E4F96D361BDE
          2B1569A6666A76D17D2BE050A5D0AB1AC6046070903774BD15D76BB59A365463
          EBB4161BEEB603ECCA3C3E30C03B659E55906F15633B4046356D31DF9241CC1D
          DEC9185B800315E38245784942D56D71DC487B4DC4B8DF048C12608A4E3044C2
          0D129E90A048E67BF5234D76A8DD51EDA509608C0D93AECF07CC8C035B39E0E8
          814E4BEFBE68FC88FE4E00B363C07616384E99B54FF2169A807EB38732089869
          8ADF42CE867915E495E8517591789DBCEF5DDFC65FB962FBE11CAE7AA4000000
          0049454E44AE426082}
      end>
    Left = 248
    Top = 112
    Bitmap = {}
  end
end

PNGImageList: https://github.com/TurboPack/PNGComponents

您代码中的主要错误是您没有为所选行和带有图像的子项绘制背景(clHighLight 颜色)。

此外,我删除了整个 DrawPicOnListViewSubItem() 过程,因为我能够将它减少到只有几行。我认为有一些试用代码。

现在的ListView1CustomDrawSubItem()程序如下:

procedure TForm1.ListView1CustomDrawSubItem(Sender: TCustomListView;
  Item: TListItem; SubItem: Integer; State: TCustomDrawState;
  var DefaultDraw: Boolean);
var
  R: TRect;
  C: TCanvas;
begin
  ListView_GetSubItemRect(Sender.Handle, Item.Index, SubItem, LVIR_BOUNDS, @R);

  C := Sender.Canvas;

  if cdsSelected in State then
  begin
    C.Brush.Color := clHighLight;
    C.FillRect(R);
  end;

  if SubItem = 1 then
  begin
    ImageList_Draw(PngImageList1.Handle, 0, C.Handle, R.Left+(R.Width-PngImageList1.Width) div 2, R.Top, ILD_TRANSPARENT);
    DefaultDraw := False;
  end;

end;