由于虚拟模式下的列表视图事先不知道项目标题(因为它只要求可见区域的数据),它也无法知道最宽的标题的宽度,所以这就是为什么要设置自动大小标志的原因LVM_SETCOLUMNWIDTH http://msdn.microsoft.com/en-us/library/windows/desktop/bb761163%28v=vs.85%29.aspx行为方式是这样的。
因此,唯一的方法是编写一个自定义函数,它将查询所有数据,测量所有未来标题的文本宽度并将列宽设置为最宽的值。
以下示例展示了如何执行此操作。它使用ListView_GetStringWidth http://msdn.microsoft.com/en-us/library/windows/desktop/bb775002%28v=vs.85%29.aspx用于文本宽度计算的宏(这似乎是最自然的方法)。然而问题是文本填充的值。正如文档中所述:
ListView_GetStringWidth 宏返回精确的宽度(以像素为单位),
指定字符串的。如果使用返回的字符串宽度作为
调用 ListView_SetColumnWidth 宏时的列宽,
字符串将被截断。检索可以的列宽
包含字符串而不截断它,您必须向
返回的字符串宽度。
但他们没有提到如何获取填充值(并且似乎they won't http://connect.microsoft.com/VisualStudio/feedback/details/215618/listview-getstringwidth-documentation-incomplete这样做)。有些人说(例如here http://www.dotnet247.com/247reference/msgs/45/228922.aspx),使用 6 px 作为项目的填充,使用 12 px 作为子项目的填充就足够了,但事实并非如此(至少对于 Windows 7 上的这个示例)。
///////////////////////////////////////////////////////////////////////////////
///// List View Column Autosize (Virtual Mode) ////////////////////////////
///////////////////////////////////////////////////////////////////////////////
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, StdCtrls,
Forms, Dialogs, StrUtils, ComCtrls, CommCtrl;
type
TSampleRecord = record
Column1: string;
Column2: string;
Column3: string;
end;
TSampleArray = array [0..49] of TSampleRecord;
type
TForm1 = class(TForm)
Button1: TButton;
ListView1: TListView;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
SampleArray: TSampleArray;
procedure AutoResizeColumn(const AListView: TListView;
const AColumn: Integer);
procedure OnListViewData(Sender: TObject; Item: TListItem);
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
///////////////////////////////////////////////////////////////////////////////
///// TForm1.AutoResizeColumn - auto-size column //////////////////////////
///////////////////////////////////////////////////////////////////////////////
// AListView - list view object instance
// AColumn - index of the column to be auto-sized
procedure TForm1.AutoResizeColumn(const AListView: TListView;
const AColumn: Integer);
var
S: string;
I: Integer;
MaxWidth: Integer;
ItemWidth: Integer;
begin
// set the destination column width to the column's caption width
// later on we'll check if we have a wider item
MaxWidth := ListView_GetStringWidth(AListView.Handle,
PChar(AListView.Columns.Items[AColumn].Caption));
// iterate through all data items and check if their captions are
// wider than the currently widest item if so then store that value
for I := 0 to High(SampleArray) do
begin
case AColumn of
0: S := SampleArray[I].Column1;
1: S := SampleArray[I].Column2;
2: S := SampleArray[I].Column3;
end;
ItemWidth := ListView_GetStringWidth(AListView.Handle, PChar(S));
if MaxWidth < ItemWidth then
MaxWidth := ItemWidth;
end;
// here is hard to say what value to use for padding to prevent the
// string to be truncated; I've found the suggestions to use 6 px
// for item caption padding and 12 px for subitem caption padding,
// but a few quick tests confirmed me to use at least 7 px for items
// and 14 px for subitems
if AColumn = 0 then
MaxWidth := MaxWidth + 7
else
MaxWidth := MaxWidth + 14;
// and here we set the column width with caption padding included
AListView.Columns.Items[AColumn].Width := MaxWidth;
end;
///////////////////////////////////////////////////////////////////////////////
///// TForm1.FormCreate - setup the list view and fill custom data ////////
///////////////////////////////////////////////////////////////////////////////
procedure TForm1.FormCreate(Sender: TObject);
var
I: Integer;
begin
ListView1.ViewStyle := vsReport;
ListView1.Columns.Add.Caption := 'Column 1';
ListView1.Columns.Add.Caption := 'Column 2';
ListView1.Columns.Add.Caption := 'Column 3';
ListView1.OwnerData := True;
ListView1.OnData := OnListViewData;
ListView1.Items.Count := High(SampleArray);
for I := 0 to High(SampleArray) do
begin
SampleArray[I].Column1 := 'Cell [0, ' + IntToStr(I) + '] ' +
DupeString('x', I);
SampleArray[I].Column2 := 'Cell [1, ' + IntToStr(I) + '] ' +
DupeString('x', High(SampleArray) - I);
SampleArray[I].Column3 := '';
end;
end;
///////////////////////////////////////////////////////////////////////////////
///// TForm1.FormCreate - custom handler for OnData event /////////////////
///////////////////////////////////////////////////////////////////////////////
procedure TForm1.OnListViewData(Sender: TObject; Item: TListItem);
begin
Item.Caption := SampleArray[Item.Index].Column1;
Item.SubItems.Add(SampleArray[Item.Index].Column2);
Item.SubItems.Add(SampleArray[Item.Index].Column3);
end;
///////////////////////////////////////////////////////////////////////////////
///// TForm1.Button1Click - auto-resize all 3 columns /////////////////////
///////////////////////////////////////////////////////////////////////////////
procedure TForm1.Button1Click(Sender: TObject);
begin
AutoResizeColumn(ListView1, 0);
AutoResizeColumn(ListView1, 1);
AutoResizeColumn(ListView1, 2);
end;
end.