【问题标题】:Implement Windows Thumbnail via TListView通过 TListView 实现 Windows 缩略图
【发布时间】:2018-10-28 04:04:48
【问题描述】:

我正在使用 Delphi XE3 并希望实现 Windows 缩略图样式以通过 TListView 控件显示图像列表。

我需要的如下:

图像显示为缩略图样式,每张图像下方都有一个标题。当我点击图片时,图片连同标题将显示为选中...

为了提高性能,我不想预先将所有图像加载到图像列表中,而是在图像要显示时加载。因此,我正在考虑使用 OnCustomDrawItem 和 OnAdvancedCustomDrawItem。

下面是我计划的一个非常简单的版本(我将列表视图的样式设置为vsIcon):

    procedure TForm1.FormCreate(Sender: TObject);
    var
      ListItem1: TListItem;
    begin
      ListItem1 := ListView1.Items.Add;

      ListItem1.Caption := 'Chrysanthemum';
    end;

    procedure TForm1.ListView1AdvancedCustomDrawItem(Sender: TCustomListView;
      Item: TListItem; State: TCustomDrawState; Stage: TCustomDrawStage;
      var DefaultDraw: Boolean);
    var
      JPEG: TJPEGImage;
      R: TRect;
    begin
    {
      R := Item.DisplayRect(drBounds);

      JPEG := TJPEGImage.Create;

      JPEG.LoadFromFile('C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum (2).jpg');

      Sender.Canvas.StretchDraw(R, JPEG);
    }
    end;

    procedure TForm1.ListView1CustomDrawItem(Sender: TCustomListView;
      Item: TListItem; State: TCustomDrawState; var DefaultDraw: Boolean);
    var
      JPEG: TJPEGImage;
      R: TRect;
    begin
      R := Item.DisplayRect(drBounds);

      JPEG := TJPEGImage.Create;

      JPEG.LoadFromFile('C:\Users\Public\Pictures\Sample Pictures\Chrysanthemum (2).jpg');

      Sender.Canvas.StretchDraw(R, JPEG);
    end;

但结果并不理想,如下:

  1. 我找不到设置每个图标大小的方法。 (所有图标都将具有相同的大小)。

  2. 我尝试将代码放在 OnCustomDrawItem 和 OnAdvancedCustomDrawItem 中。我无法弄清楚这两者之间有多大区别。 Advancedxxx 版本中唯一的主要区别在于,标题是可编辑的。我不明白为什么。

  3. 标题不显示在图像下方,而是显示在图像中间,这是不希望的。如何解决?

谢谢

【问题讨论】:

  • 查看OnDrawItem() 事件的文档。有一条注释说:注意:列表视图接收其他几个自定义绘制事件,包括 OnCustomDraw、OnCustomDrawItem、OnCustomDrawSubItem、OnAdvancedCustomDraw、OnAdvancedCustomDrawItem 和 OnAdvancedCustomDrawSubItem。与 OnDrawItem 不同,这些其他事件的发生与 OwnerDraw 属性的值无关。 ... 所以,使用这些其他事件中的任何一个。
  • 如果您想在TListView 中显示大量图像,请考虑在虚拟模式下使用它,将OwnerData 属性设置为trueExample of owner-drawing items in TListView.
  • @Dima 有没有关于 TListView 所有者绘图的完整示例代码?您提供的链接使用 AdvancedCustomDrawItem,我检查了文档 docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/…,它说“要在其他阶段(例如在绘制列表项之后)增加默认绘图过程,请改用 OnAdvancedCustomDrawItem 事件。”所以看起来这个例子使用了错误的事件处理程序?
  • 不,示例是正确的。如果您将使用OnCustomDrawItem 事件,您应该从头开始绘制项目,将DefaultDraw 设置为false,否则您将在绘图上完成默认绘图。 OnAdvancedCustomDrawItem 事件允许您控制绘制阶段 cdPrePaintcdPostPaint。在我看来,所有者绘制TListView 是一种更优雅的方式,对于这个简单的例子来说就足够了。但是是的,如果您要在绘图事件中进行重绘,最好使用OnCustomDrawItem 事件,因为它发生之前 实际项目绘图。使用什么取决于您的需求。
  • 我建议你看看this component。它可以将图像显示为标准TListView(但它不具备后者的所有功能)。在您指定要在其中搜索的文件夹后,它会缓存图像。这东西值得一试。可能对于您的任务来说,它可能比使用标准 TListView 跳舞更有用。

标签: listview delphi delphi-xe3


【解决方案1】:

仅当关联图标实际显示在列表视图中时,附加代码才会将图像(在本例中为图标)加载到分配给 TListView 的LargeImages 属性的 TImageList 中。主要是将列表视图的OwnerData 属性设置为TRUE,并为OnData 事件创建事件处理程序。与列表视图中的项目并行,程序维护列表视图中的项目列表,该列表与列表视图中的实际列表同步,在本例中为 TStringList。在其Objects 属性中,我存储相关图标资源的索引(如果该资源已加载并添加到 TImageList 中)。如果图标资源没有被加载,这发生在LoadIconFromFile函数中,图标在TImageList中的索引被存储在TStringList中。

TListView 中图标和文本的实际绘制完全由控件本身处理,代码既不处理OnDraw 也不处理任何OnCustomDraw* 事件。只需将 TImageList 中的图像大小设置为您要显示的位图大小并相应地创建它们。

较旧的 Delphi 版本包含一个名为“VirtualListView.dpr”的示例项目,它非常有助于了解各种 OnData* 事件何时被触发以及如何正确使用它们。

unit MainFormU;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ImgList, StdCtrls, ComCtrls;

type
  TForm1 = class(TForm)
    Icons_LV: TListView;
    Label1: TLabel;
    Large_IL: TImageList;
    procedure Icons_LVData(Sender: TObject; Item: TListItem);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    FileList : TStringList;

    procedure FillListView;
    function LoadIconFromFile (const sFileName: String;
                               out iIndex: Integer) : Boolean;
  end;

var Form1 : TForm1;

implementation

{$R *.dfm}

uses ShellApi;

const
  cWinSysDir = 'c:\windows\system32\';

procedure TForm1.FormCreate (Sender: TObject);
begin
  FileList := TStringList.Create;
  FillListView;
end;

procedure TForm1.FormDestroy (Sender: TObject);
begin
  FileList.Free;
end;

procedure TForm1.Icons_LVData (Sender: TObject; Item: TListItem);

var iIndex : Integer;

begin
  if (Item.Index >= FileList.Count) then
    exit;

  Item.Caption := FileList [Item.Index];

  if (FileList.Objects [Item.Index] = TObject (-1)) then
  begin
    if not (LoadIconFromFile (cWinSysDir + Item.Caption, iIndex)) then
      iIndex := 0;

    FileList.Objects [Item.Index] := TObject (iIndex);
  end { if }
  else iIndex := Integer (FileList.Objects [Item.Index]);

  Item.ImageIndex := iIndex
end;

procedure TForm1.FillListView;

var SR : TSearchRec;

begin
  FillChar (SR, SizeOf (TSearchRec), #0);

  if (FindFirst (cWinSysDir + '*.exe', faAnyFile, SR) = 0) then
    repeat
      FileList.AddObject (SR.Name, TObject ((-1)));
    until (FindNext (SR) <> 0);

  FindClose (SR);
  Icons_LV.Items.Count := FileList.Count;
end;

function TForm1.LoadIconFromFile (const sFileName: String;
                                  out iIndex: Integer) : Boolean;

var
  hIcon : Windows.HICON;
  Icon : TIcon;

begin
  Result := false;

  if (ExtractIcon (MainInstance, PChar (sFileName), UInt ((-1))) > 0) then
  begin
{$IFDEF DEBUG}
    OutputDebugString (PChar (Format ('LoadIconFromFile "%s"', [sFileName])));
{$ENDIF}
    hIcon := ExtractIcon (MainInstance, PChar (sFileName), 0);

    if (hIcon <> 0) then
    begin
      Icon := TIcon.Create;
      Icon.Handle := hIcon;
      iIndex := Large_IL.AddIcon (Icon);
      Icon.Free;
      Result := true;
    end; { if }
  end { if }
end;

end.

完整示例可供下载here

【讨论】:

  • OP 希望显示 图像缩略图 而不是 *.exe-files 的图标。
  • 过程相同,只需将“LoadIconFromFile”方法替换为将图像加载到TImageList中的方法即可。从与问题一起发布的代码中,我认为创建缩略图不是问题。
  • 我之前的评论很愚蠢,因为我错误地说明了发生内存泄漏的位置。此外,仅在TImageList 中加载图像不会创建缩略图。顺便说一句,我已经测试了您的代码并且存在内存泄漏。这是因为您没有在 LoadIconFromFile() 方法中释放 TIcon 的实例。以这种方式更改TIcon 创建的代码:Icon := TIcon.Create; try Icon.Handle := hIcon; iIndex := Large_IL.AddIcon (Icon); finally Icon.Free; end;
  • @Dima:我已经修复了内存泄漏。感谢您指出。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-11
  • 2011-10-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多