三大步驟
- 取得圖示控制碼:`SHGetFileInfo` 依副檔名/檔案屬性從系統登錄取得圖示。
- 載入至 ImageList:`ImageList_AddIcon` 將 `HICON` 加入 `TImageList`,取得索引。
- OwnerDraw 繪製:對 `ComboBox`、`ListBox`、`ListView` 等設定 `Style = csOwnerDrawVariable`/`lbOwnerDrawVariable`,在 `OnDrawItem` 事件中用 `ImageList.Draw` + `Canvas.TextOut` 繪製圖示+文字。
核心 API 宣告 (ShellAPI)
function SHGetFileInfo(pszPath: PAnsiChar; dwFileAttributes: DWORD;
var psfi: TSHFileInfo; cbFileInfo: UINT; uFlags: UINT): DWORD; stdcall;
type
TSHFileInfo = record
hIcon: HICON; // 圖示控制碼
iIcon: Integer; // 系統映像清單索引
dwAttributes: DWORD; // 檔案屬性
szDisplayName: array[0..MAX_PATH-1] of AnsiChar; // 顯示名稱
szTypeName: array[0..79] of AnsiChar; // 類型名稱
end;
常用 Flags 組合
| Flag | 用途 |
| SHGFI_ICON | 取得圖示控制碼 |
| SHGFI_SMALLICON / SHGFI_LARGEICON | 小圖示(16x16) / 大圖示(32x32) |
| SHGFI_DISPLAYNAME | 取得顯示名稱 (含長檔名) |
| SHGFI_TYPENAME | 取得類型描述 (如 "Text Document") |
| SHGFI_USEFILEATTRIBUTES | 不需實體檔案存在,僅依屬性/副檔名取得通用圖示 |
完整範例:列出目錄檔案並顯示圖示 (ListBox OwnerDraw)
procedure TForm1.LoadFiles(const Dir: string);
var
SR: TSearchRec;
FI: TSHFileInfo;
Idx: Integer;
begin
ListBox1.Clear;
ImageList1.Clear;
ImageList1.Width := 16; ImageList1.Height := 16; // 小圖示
if FindFirst(IncludeTrailingPathDelimiter(Dir) + '*.*', faAnyFile, SR) = 0 then
repeat
if (SR.Attr and faDirectory) = 0 then begin // 僅檔案
// 1. 取得圖示
SHGetFileInfo(PAnsiChar(Dir + SR.Name), 0, FI, SizeOf(FI),
SHGFI_ICON or SHGFI_SMALLICON or SHGFI_DISPLAYNAME);
// 2. 加入 ImageList
Idx := ImageList_AddIcon(ImageList1.Handle, FI.hIcon);
// 3. 加入 ListBox (儲存索引於 Objects)
ListBox1.Items.AddObject(FI.szDisplayName, TObject(Idx));
DestroyIcon(FI.hIcon); // 釋放系統資源
end;
until FindNext(SR) <> 0;
FindClose(SR);
end;
procedure TForm1.ListBox1DrawItem(Control: TWinControl; Index: Integer;
Rect: TRect; State: TOwnerDrawState);
var
Lb: TListBox;
Idx: Integer;
begin
Lb := Control as TListBox;
Lb.Canvas.FillRect(Rect);
Idx := Integer(Lb.Items.Objects[Index]);
// 繪製圖示
ImageList1.Draw(Lb.Canvas, Rect.Left + 2, Rect.Top + 1, Idx);
// 繪製文字
Lb.Canvas.TextOut(Rect.Left + 20, Rect.Top + 1, Lb.Items[Index]);
end;
進階技巧
- 大圖示模式:`ImageList.Width/Height := 32`,`SHGFI_LARGEICON`,`ListView` 設 `ViewStyle = vsIcon`。
- 虛擬資料夾 (我的電腦、網路芳鄰) 需 `SHGFI_PIDL` 配合 `SHGetFolderLocation`/`SHBindToParent` 取得 PIDL,較複雜。
- 快取系統映像清單**:`SHGetFileInfo` 配合 `SHGFI_SYSICONINDEX` 直接取得系統 `ImageList` 索引,避免重複載入,但需處理進程間共用映像清單。
- 高 DPI**:VCL 高 DPI 感知下,`ImageList` 需對應縮放比例 (96/120/144 DPI) 準備多尺寸圖示。