首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> Delphi

Delphi 實戰:在程序中使用動態圖標(SHGetFileInfo + ImageList 完整指南)

     編輯:Delphi

場景

在 TComboBox、TListBox、TListView、TTreeView 等控件中,根據文件類型動態顯示系統關聯圖標(如 .txt 顯示記事本圖標、.exe 顯示程序圖標),實現類似資源管理器的視覺效果。

三步核心流程

  1. 獲取圖標句柄:SHGetFileInfo (ShellAPI) 根據文件路徑/屬性獲取 HICON。
  2. 加載到 ImageList:ImageList_AddIcon (CommCtrl) 將 HICON 放入 TImageList,獲得索引。
  3. 綁定顯示:控件 Style := csOwnerDrawVariable/lbOwnerDrawVariable + OnDrawItem 繪製圖標+文本,或直接設 ImageList 屬性 + ImageIndex。

核心 API 詳細參數

SHGetFileInfo


function SHGetFileInfo(
  pszPath: PAnsiChar;           // 文件路徑或 PIDL
  dwFileAttributes: DWORD;      // 文件屬性 (配合 SHGFI_USEFILEATTRIBUTES)
  var psfi: TSHFileInfo;        // 輸出結構
  cbFileInfo: UINT;             // 結構大小
  uFlags: UINT                  // 請求信息類型組合
): DWORD;

TSHFileInfo 關鍵字段

字段類型含義
hIconHICON圖標句柄 (核心輸出)
iIconInteger系統圖標索引 (可用於 ImageList 直接映射)
dwAttributesDWORD文件屬性
szDisplayNamearray[0..MAX_PATH-1] of AnsiChar顯示名稱
szTypeNamearray[0..79] of AnsiChar類型名稱 (如「Text Document」)

常用 uFlags 組合

標志值用途
SHGFI_ICON$100獲取 hIcon
SHGFI_DISPLAYNAME$200獲取顯示名
SHGFI_TYPENAME$400獲取類型名
SHGFI_ATTRIBUTES$800獲取屬性
SHGFI_LARGEICON$0大圖標 (32x32 默認)
SHGFI_SMALLICON$1小圖標 (16x16)
SHGFI_USEFILEATTRIBUTES$10不訪問磁盤,僅按擴展名獲取圖標 (極快)
SHGFI_SYSICONINDEX$4000返回 iIcon (系統圖標索引),不返回 hIcon

ImageList_AddIcon


function ImageList_AddIcon(himl: HIMAGELIST; hIcon: HICON): Integer;
// 返回圖標在 ImageList 中的索引,-1 表示失敗

完整實戰代碼:ComboBox 動態顯示文件圖標


unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    ComboBox1: TComboBox;
    ImageList1: TImageList;
    btnLoad: TButton;
    procedure FormCreate(Sender: TObject);
    procedure btnLoadClick(Sender: TObject);
    procedure ComboBox1DrawItem(Control: TWinControl; Index: Integer;
      Rect: TRect; State: TOwnerDrawState);
  private
    procedure LoadFiles(const Dir: string);
    function GetFileIconIndex(const FileName: string): Integer;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  // 1. ImageList 設置小圖標尺寸
  ImageList1.Width := 16;
  ImageList1.Height := 16;
  // 關鍵:創建時指定尺寸,大圖標需 32x32
  ImageList1.Handle := ImageList_Create(16, 16, ILC_COLOR32 or ILC_MASK, 0, 10);

  // 2. ComboBox 設置 OwnerDraw 以繪製圖標+文本
  ComboBox1.Style := csOwnerDrawVariable;
  ComboBox1.ItemHeight := 20; // 足夠高度容納圖標+文本
end;

procedure TForm1.btnLoadClick(Sender: TObject);
begin
  with TFileOpenDialog.Create(Self) do
  try
    Options := [fdoPickFolders];
    if Execute then
      LoadFiles(FileName);
  finally
    Free;
  end;
end;

procedure TForm1.LoadFiles(const Dir: string);
var
  SR: TSearchRec;
  Idx: Integer;
begin
  ComboBox1.Items.BeginUpdate;
  ImageList1.Clear;
  try
    // 重新創建 Handle 確保尺寸生效
    ImageList1.Handle := ImageList_Create(16, 16, ILC_COLOR32 or ILC_MASK, 0, 10);
    ComboBox1.Items.Clear;

    if FindFirst(IncludeTrailingPathDelimiter(Dir) + '*.*', faAnyFile, SR) = 0 then
    try
      repeat
        if (SR.Name <> '.') and (SR.Name <> '..') then
        begin
          Idx := GetFileIconIndex(IncludeTrailingPathDelimiter(Dir) + SR.Name);
          ComboBox1.Items.AddObject(SR.Name, TObject(Idx)); // 文本 + 圖標索引
        end;
      until FindNext(SR) <> 0;
    finally
      FindClose(SR);
    end;
  finally
    ComboBox1.Items.EndUpdate;
  end;
end;

function TForm1.GetFileIconIndex(const FileName: string): Integer;
var
  ShInfo: TSHFileInfo;
  hIcon: HICON;
begin
  Result := -1;
  // 使用 SHGFI_USEFILEATTRIBUTES 不訪問磁盤,極快
  if SHGetFileInfo(PChar(FileName), FILE_ATTRIBUTE_NORMAL, ShInfo,
     SizeOf(ShInfo), SHGFI_ICON or SHGFI_SMALLICON or SHGFI_USEFILEATTRIBUTES) <> 0 then
  begin
    hIcon := ShInfo.hIcon;
    if hIcon <> 0 then
    begin
      Result := ImageList_AddIcon(ImageList1.Handle, hIcon);
      DestroyIcon(hIcon); // SHGetFileInfo 返回的 hIcon 需手動銷毀
    end;
  end;
end;

procedure TForm1.ComboBox1DrawItem(Control: TWinControl; Index: Integer;
  Rect: TRect; State: TOwnerDrawState);
var
  Idx: Integer;
  Text: string;
  ImgX, ImgY, TxtX: Integer;
begin
  with (Control as TComboBox) do
  begin
    Canvas.FillRect(Rect); // 清背景

    Text := Items[Index];
    Idx := Integer(Items.Objects[Index]); // 取回圖標索引

    // 計算佈局
    ImgX := Rect.Left + 2;
    ImgY := Rect.Top + (Rect.Height - ImageList1.Height) div 2;
    TxtX := ImgX + ImageList1.Width + 4;

    // 繪製圖標
    if (Idx >= 0) and (Idx < ImageList1.Count) then
      ImageList1.Draw(Canvas, ImgX, ImgY, Idx);

    // 繪製文本
    Canvas.TextOut(TxtX, Rect.Top + (Rect.Height - Canvas.TextHeight(Text)) div 2, Text);

    // 選中態高亮
    if odSelected in State then
      Canvas.FrameRect(Rect);
  end;
end;

end.

關鍵技術點深度解析

1. SHGFI_USEFILEATTRIBUTES:離線獲取圖標的神器

  • 不讀取磁盤,僅根據擴展名查詢註冊表關聯,毫秒級響應,適合批量處理大量文件。
  • 缺點:無法獲取「我的電腦」、「網絡鄰居」等虛擬文件夾圖標,也無法識別無擴展名文件的真實類型。

2. 圖標句柄所有權:DestroyIcon 必須調用

SHGetFileInfo 返回的 hIcon 由調用者擁有,ImageList_AddIcon 內部複製圖標位圖,**不接管句柄所有權**,必須在添加後 DestroyIcon(hIcon),否則 GDI 對象洩漏。

3. csOwnerDrawVariable vs csOwnerDrawFixed

  • Variable:每項高度可異,觸發 OnMeasureItem + OnDrawItem,靈活但略慢。
  • Fixed:統一 ItemHeight,僅觸發 OnDrawItem,性能更好。圖標固定 16x16 時推薦 Fixed。

4. 系統圖標索引 (iIcon) 直接映射優化


// 獲取系統圖標索引,直接用於 ListView/TreeView 的 ImageIndex
SHGetFileInfo(PChar(FileName), 0, ShInfo, SizeOf(ShInfo),
  SHGFI_SYSICONINDEX or SHGFI_SMALLICON or SHGFI_USEFILEATTRIBUTES);
// ShInfo.iIcon 即為系統映像列表中的索引
// 需先獲取系統 ImageList Handle: SHGetFileInfo('', 0, ShInfo, SizeOf(ShInfo), SHGFI_SYSICONINDEX or SHGFI_SMALLICON);
// 然後控件.Images.Handle := ShInfo.hIcon (系統 ImageList)
// 但系統 ImageList 只讀,不能添加自定義圖標,故常用自建 ImageList + ImageList_AddIcon

5. 大圖標模式 (SHGFI_LARGEICON / 32x32)


ImageList1.Width := 32;
ImageList1.Height := 32;
ImageList1.Handle := ImageList_Create(32, 32, ILC_COLOR32 or ILC_MASK, 0, 10);
// SHGetFileInfo 使用 SHGFI_LARGEICON (默認) 獲取 32x32 hIcon

控件綁定模式對比

控件模式 A: OwnerDraw 繪製模式 B: 內建 ImageList 屬性
TComboBoxcsOwnerDrawFixed + OnDrawItem不支援 (無 Images 屬性)
TListBoxlbOwnerDrawFixed + OnDrawItem不支援
TListViewOnCustomDrawItem (vsReport)SmallImages/LargeImages + Item.ImageIndex ✓
TTreeViewOnCustomDrawItemImages + Node.ImageIndex/SelectedIndex ✓
TStatusBarPanel.Style := psOwnerDraw + OnDrawPanel不直接支援

總結

動態圖標三部曲:SHGetFileInfo 獲取 hIcon → ImageList_AddIcon 入列表 → 控件綁定繪製。關鍵細節:**USEFILEATTRIBUTES 離線加速、DestroyIcon 防洩漏、OwnerDraw 佈局計算、ImageList 尺寸預創建**。掌握此套流程,即可為任何文件列表、進程列表、插件管理器賦予原生系統級視覺體驗。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved