Как правильно установить размер столбца ListView в соответствии с его содержимым?
У меня есть несколько элементов управления представления списка (TListView), которые используются для отображения данных. Все эти представления списка установлены в режим "Детализация", и у всех есть TImageList, назначенный их свойствам "SmallIcons".
Я пытаюсь установить ширину этих столбцов на основе их содержимого точно так же, как если бы пользователь дважды щелкнул по ползунку разделителя в конце каждого из заголовков столбцов.
Во-первых, я попытался установить ширину столбца в "-1" и "-2" для их автоматического определения размера: не только это не сработало идеально (некоторые столбцы содержат локальные символы - я использую D6 и это означает строки ANSI - слишком низкие), но это также сделало отображение столбца чрезвычайно медленным (до 30 секунд, чтобы отобразить представление списка с 6 столбцами и 150 элементами, когда оно мгновенно с фиксированной шириной).
Я попытался использовать GetTextExtent для каждой ячейки, чтобы получить ожидаемую ширину текста, добавив некоторое поле (от 2 до 10 пикселей) и увеличить ширину столбца, если она меньше, чем рассчитанная ширина текста. Специальная обработка применяется к первому столбцу (Items.caption), чтобы учесть отображение значка (я добавляю ширину значка плюс поле к ширине текста ячейки).
Это тоже не сработало: во многих случаях (например, отображение даты в формате "гггг / мм / дд чч:nn:ss" приводит к тому, что текст слишком велик для размещения в столбце).
Думая, что проблема может быть вызвана механизмом оконных тем, я переключился на использование GetThemeTextExtent вместо GetTextExtent, но получил тот же результат.
Единственная вещь, которая, кажется, работает - это добавить произвольное большое поле (20 пикселей) к ширине каждого столбца, но, конечно, это приводит к столбцам, которые больше, чем они должны быть.
Итак, есть ли альтернативная стратегия? Мне не нужно ничего, кроме того, что вычислит правильную ширину один раз: когда список будет заполнен впервые. Код за "щелчок на разделителе столбцов" работает просто отлично, но я не могу найти, как вызвать его по коду (ну, я думаю, я мог бы отправлять сообщения двойного щелчка в заголовок прямо как хак)
Для пояснения, вот что я попробовал следующий код:
(в случае вызова происходит ListView.canvas.Font.Assign(ListView.font)
, Это не в этих функциях, потому что достаточно одного присваивания, но код зацикливается на всех неавтоматизированных столбцах списка).
редактировать
Моя первая попытка использования Windows Theme API:
function _GetTextWidth1(AText: widestring; IsHeader: boolean = false): Integer;
var
ATheme: HTheme;
rValue: TRect;
iPartID: integer;
AWidetext: WideString;
const
LVP_GROUPHEADER = 6;
begin
// try to get text width using theme API
ZeroMemory(@rValue, SizeOf(rValue));
ATheme := OpenThemeData(ListView.Handle, 'LISTVIEW');
try
if not IsHeader then
iPartID := LVP_LISTITEM
else
iPartID := LVP_GROUPHEADER;
AWidetext := AText;
GetThemeTextExtent( ATheme,
ListView.Canvas.Handle,
iPartID,
LIS_NORMAL,
PWideChar(AWidetext),
-1,
DT_LEFT or DT_SINGLELINE or DT_CALCRECT,
nil,
rValue
);
finally // wrap up
CloseThemeData(ATheme);
end; // try/finally
result := rValue.Right;
end;
следующая попытка с использованием DrawText/DrawTextW:
function _GetTextWidth2(AText: widestring; IsHeader: boolean = false): Integer;
var
rValue: TRect;
lFlags: Integer;
begin
// try to get text width using DrawText/DrawTextW
rValue := Rect(0, 0, 0, 0);
lFlags := DT_CALCRECT or DT_EXPANDTABS or DT_NOPREFIX or DT_LEFT or DT_EXTERNALLEADING;
DrawText(ListView.canvas.Handle, PChar(AText), Length(AText), rValue, lFlags);
//DrawTextW(ListView.canvas.Handle, PWideChar(AText), Length(AText), rValue, lFlags);
result := rValue.Right;
end;
Третья попытка с использованием функции TextWidth delphi
function _GetTextWidth3(AText: widestring; IsHeader: boolean = false): Integer;
begin
// try to get text width using delphi wrapped around GetTextExtentPoint32
result := ListView.canvas.TextWidth(Atext);
end;
Во всех случаях я добавляю поле к результирующей ширине: я пробовал значения до 20 пикселей. Я также принимаю во внимание возможность того, что представление использует значки (в этом случае я добавляю ширину значка плюс поле снова к первому столбцу).
1 ответ
Вы можете использовать метод canvas.TextWidth. Но обязательно используйте холст TListView (не другой, т.е. TForm) и сначала назначьте шрифт холсту из TListView. Например:
var
s: integer;
begin
ListView1.AddItem('test example item', nil);
ListView1.canvas.Font.Assign(ListView1.font);
s := ListView1.canvas.TextWidth(ListView1.Items[0].Caption) + 10; //this "+10" is a small additional margin
if s > ListView1.Columns[0].Width then
ListView1.Columns[0].Width := s;
Он отлично работает для меня.