Когда я использую TListView (ViewStyle = vsReport), я могу автоматически подбирать ширину столбцов, устанавливая LVSCW_AUTOSIZE
или LVSCW_AUTOSIZE_USEHEADER
в свойстве Width каждого столбца, теперь я начинаю использовать Listview в виртуальном режиме, но ширина столбцов не изменяется в соответствии с этими значениями. Итак, вопрос: как я могу настроить ширину столбцов, чтобы они соответствовали содержимому или заголовку, когда lisvtiew находится в виртуальном режиме?
как автоматически изменить ширину столбцов списка в виртуальном режиме?
Ответы (3)
Поскольку представление списка в виртуальном режиме не знает заранее заголовки элементов (поскольку оно запрашивает только данные видимой области), оно также не может знать ширину самого широкого из них, поэтому по этой причине флаг авторазмера элемента < href="http://msdn.microsoft.com/en-us/library/windows/desktop/bb761163%28v=vs.85%29.aspx" rel="noreferrer">LVM_SETCOLUMNWIDTH
ведет себя так .
Таким образом, единственный способ — написать пользовательскую функцию, которая будет запрашивать все ваши данные, измерять ширину текста всех будущих подписей и устанавливать ширину столбца на значение самой широкой.
В следующем примере показано, как это сделать. Он использует ListView_GetStringWidth
макрос для расчета ширины текста (похоже, это самый естественный способ). Однако проблема заключается в значении текстового заполнения. Как указано в документации:
Макрос ListView_GetStringWidth возвращает точную ширину в пикселях указанной строки. Если вы используете возвращаемую ширину строки в качестве ширины столбца при вызове макроса ListView_SetColumnWidth, строка будет усечена. Чтобы получить ширину столбца, который может содержать строку без ее усечения, необходимо добавить отступы к возвращаемой ширине строки.
Но они не упомянули там, как получить значение заполнения (и кажется they won't
сделать это). Некоторые говорят (например, here
), что для отступ элемента и 12 пикселей для отступа подэлемента, но это не так (по крайней мере, для этого примера в 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.
GetThemeData
? Может быть удобно найти значение заполнения (конечно, только если у вас будут включены темы), но все же.
- person TLama; 14.03.2012
Рассмотрим этот вспомогательный функциональный модуль, написанный RRUZ.
Выдержка из вспомогательных функций:
procedure AutoResizeColumn(const Column:TListColumn;const Mode:Integer=LVSCW_AUTOSIZE_BESTFIT);
procedure AutoResizeColumns(const Columns : Array of TListColumn;const Mode:Integer=LVSCW_AUTOSIZE_BESTFIT);
procedure AutoResizeListView(const ListView : TListView;const Mode:Integer=LVSCW_AUTOSIZE_BESTFIT);
Режим (параметр) может быть:
- LVSCW_AUTOSIZE_BESTFIT
- LVSCW_AUTOSIZE
- LVSCW_AUTOSIZE_USEHEADER
Я надеюсь, что это послужит хорошей отправной точкой для вашего требования.
Вот еще одно возможное решение, позволяющее избежать слишком узких столбцов. Однако это требует некоторых знаний о данных, которые вам нужно отобразить, поэтому это не общее решение.
Создайте ListViewItem, используя самые длинные/самые широкие элементы данных. Переключитесь в невиртуальный режим и добавьте только этот максимальный ListViewItem. Автоматически настраивайте ширину столбца на основе максимального элемента, затем удаляйте максимальный элемент и возвращайтесь в виртуальный режим. Например.:
// build a ListViewItem with longest data items
string[] items = new string[2];
items[0] = "999999"; // number
items[1] = "99:59:59.999"; // time hh:mm:ss.ttt
ListViewItem lviMax = new ListViewItem (items);
lv.VirtualMode = false; // switch to non-virtual mode
lv.Items.Clear (); // empty the row/line collection
lv.Visible = false; // so user doesnt see the fake values
lv.Items.Add (lviMax); // add line(s) with longest possible data items
lv.AutoResizeColumns (ColumnHeaderAutoResizeStyle.ColumnContent); // adjust column width
lv.AutoResizeColumns (ColumnHeaderAutoResizeStyle.HeaderSize); // adjust column width
lv.Items.Clear (); // empty row/line collection
lv.Visible = true;
lv.VirtualMode = true; // switch back to virtual mode
В зависимости от значений вашего примера формата некоторые столбцы теперь могут быть слишком широкими, но, по крайней мере, ни один столбец не будет слишком узким.
LVSCW_AUTOSIZE
ведет себя таким образом). А что касается дополнительных пикселей, это значение является спекулятивным (даже MS не говорит, как их вычислить, см. Мой ответ ниже). - person TLama   schedule 16.02.2012