在 TListView 的 vsReport (詳細報表) 模式下,動態創建列標題並逐行填充數據集記錄,實現類似 TDBGrid 但更輕量、可定制的數據展示。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, DB, DBTables, ComCtrls;
type
TForm1 = class(TForm)
Query1: TQuery;
ListView1: TListView;
procedure FormCreate(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
i: Integer;
Col: TListColumn;
Item: TListItem;
begin
// 1. 設置數據源 (DBDEMOS 範例數據庫)
Query1.DatabaseName := 'DBDEMOS';
Query1.SQL.Text := 'SELECT * FROM country';
Query1.Active := True;
// 2. 切換到報表模式
ListView1.ViewStyle := vsReport;
ListView1.ReadOnly := True;
ListView1.RowSelect := True;
// 3. 動態創建列:字段名作為標題
for i := 0 to Query1.FieldCount - 1 do
begin
Col := ListView1.Columns.Add;
Col.Caption := Query1.Fields[i].FieldName;
Col.Width := 120; // 默認寬度,可根據字段類型調整
Col.Alignment := taLeftJustify;
end;
// 4. 填充數據行
Query1.First;
while not Query1.Eof do
begin
Item := ListView1.Items.Add;
// 第一列用 Caption
Item.Caption := Query1.Fields[0].AsString;
// 其餘列用 SubItems
for i := 1 to Query1.FieldCount - 1 do
Item.SubItems.Add(Query1.Fields[i].AsString);
Query1.Next;
end;
end;
end.
object Form1: TForm1
ClientHeight = 400
ClientWidth = 600
Caption = 'TListView 數據庫演示'
object ListView1: TListView
Align = alClient
ViewStyle = vsReport
ReadOnly = True
RowSelect = True
TabOrder = 0
end
object Query1: TQuery
Left = 24
Top = 24
end
end
| 任務 | API | 說明 |
|---|---|---|
| 設置視圖模式 | ViewStyle := vsReport | vsIcon/vsSmallIcon/vsList/vsReport |
| 添加列 | Columns.Add → TListColumn | 返回新列對象,設 Caption/Width/Alignment |
| 添加行 | Items.Add → TListItem | 返回新行對象 |
| 第一列數據 | Item.Caption | 字串 |
| 第 2~N 列數據 | Item.SubItems.Add/Strings[i] | TStrings 集合,索引 0 對應第 2 列 |
| 清空所有 | Items.Clear / Columns.Clear |
procedure AutoSizeColumns(LV: TListView; MaxWidth: Integer = 300);
var
i, j, W: Integer;
Canvas: TCanvas;
begin
Canvas := TControlCanvas.Create;
try
Canvas.Control := LV;
Canvas.Font := LV.Font;
for i := 0 to LV.Columns.Count - 1 do
begin
W := Canvas.TextWidth(LV.Columns[i].Caption) + 20; // 標題寬度
for j := 0 to LV.Items.Count - 1 do
begin
if i = 0 then
W := Max(W, Canvas.TextWidth(LV.Items[j].Caption) + 20)
else if i - 1 < LV.Items[j].SubItems.Count then
W := Max(W, Canvas.TextWidth(LV.Items[j].SubItems[i-1]) + 20);
end;
LV.Columns[i].Width := Min(W, MaxWidth);
end;
finally
Canvas.Free;
end;
end;
// 填充數據後調用
AutoSizeColumns(ListView1);
procedure TForm1.ListView1CustomDrawItem(Sender: TCustomListView; Item: TListItem;
State: TCustomDrawState; var DefaultDraw: Boolean);
begin
if Odd(Item.Index) then
Sender.Canvas.Brush.Color := clInfoBk // 淺黃
else
Sender.Canvas.Brush.Color := clWindow; // 白
end;
// 設置 ListView1.OwnerDraw := True 並綁定 OnCustomDrawItem
// 適合 10萬+ 行,不預存 Items,按需請求
ListView1.OwnerData := True;
ListView1.Items.Count := Query1.RecordCount; // 設置虛擬行數
// OnData 事件提供單元格文本
procedure TForm1.ListView1Data(Sender: TObject; Item: TListItem);
begin
Query1.RecNo := Item.Index + 1;
Item.Caption := Query1.Fields[0].AsString;
for i := 1 to Query1.FieldCount - 1 do
Item.SubItems.Add(Query1.Fields[i].AsString);
end;
var
FSortColumn: Integer = -1;
FSortAscending: Boolean = True;
procedure TForm1.ListView1ColumnClick(Sender: TObject; Column: TListColumn);
begin
if FSortColumn = Column.Index then
FSortAscending := not FSortAscending
else
begin
FSortColumn := Column.Index;
FSortAscending := True;
end;
// 這裡需重新查詢數據庫 ORDER BY 對應字段,或用 ClientDataSet 內存排序
RefreshData;
end;
| 現象 | 原因 | 解決 |
|---|---|---|
| 列不顯示 | ViewStyle ≠ vsReport | 設置 ListView1.ViewStyle := vsReport |
| 數據只顯示第一列 | SubItems 未添加 | 循環 Item.SubItems.Add(...) |
| 運行時列標題亂碼 | 字段名含中文/特殊字符 | 用 DisplayLabel 或自定義標題映射 |
| 性能極慢 | 逐行 Add 觸發重繪 | Items.BeginUpdate/EndUpdate 包裹填充循環 |
| 內存飆升 | 大量行未釋放 | 用 OwnerData 虛擬模式或分頁加載 |
TListView + vsReport 是 Delphi 輕量級表格展示的經典選擇:**無數據綁定依賴、完全手動控制列行、擴展性極強**。掌握 Columns.Add/Items.Add/Caption/SubItems 四大核心 API,配合 BeginUpdate/EndUpdate、虛擬模式、自繪事件,即可構建從簡單查詢結果到複雜報表的各類數據展示界面。