Windows 如何根据内容正确设置ListView列的大小?

Windows 如何根据内容正确设置ListView列的大小?,windows,delphi,winapi,delphi-6,common-controls,Windows,Delphi,Winapi,Delphi 6,Common Controls,我有许多用于显示数据的列表视图控件(TListView)。所有这些列表视图都设置为“详细”模式,并且都将TImageList指定给其“小图标”属性 我试图根据这些列的内容设置它们的宽度,就像用户双击每个列标题末尾的分隔符滑块一样 首先,我尝试将列宽设置为“-1”和“-2”以自动调整大小:这不仅不能很好地工作(一些包含本地字符的列-我使用的是D6,这意味着ANSI字符串-太低),而且还使列的显示速度非常慢(当以固定宽度即时显示时,最多30秒可显示包含6列和150项的列表视图) 我尝试在每个单元格上

我有许多用于显示数据的列表视图控件(TListView)。所有这些列表视图都设置为“详细”模式,并且都将TImageList指定给其“小图标”属性

我试图根据这些列的内容设置它们的宽度,就像用户双击每个列标题末尾的分隔符滑块一样

首先,我尝试将列宽设置为“-1”和“-2”以自动调整大小:这不仅不能很好地工作(一些包含本地字符的列-我使用的是D6,这意味着ANSI字符串-太低),而且还使列的显示速度非常慢(当以固定宽度即时显示时,最多30秒可显示包含6列和150项的列表视图)

我尝试在每个单元格上使用GetTextExtent来获得预期的文本宽度,添加一些边距(从2到10像素),如果列的宽度低于计算的文本宽度,则扩展该列的宽度。将对第一列(Items.caption)进行特殊处理,以考虑图标的显示(我将图标的宽度加上边距添加到单元格文本的宽度中)

这也不起作用:在许多情况下(例如,以“yyyy/mm/dd hh:nn:ss”格式显示日期会导致文本太大,无法放入列中)

考虑到问题可能来自窗口主题引擎,我已经切换到使用GetTheMetextent而不是GetTextExtent,但得到了相同的结果

唯一有效的方法是在每个列宽上添加任意大的边距(20个像素),当然,这会产生比实际大小更大的列

那么,有什么替代策略吗?我不需要任何东西,只需要一次计算正确的宽度:当列表第一次填充时。“单击列分隔符”后面的代码工作正常,但我找不到如何通过代码触发它(好吧,我想我可以将双击消息作为黑客直接发送到标题)

为了澄清,以下是我在以下代码中尝试的内容:

(在call case中,调用了
ListView.canvas.Font.Assign(ListView.Font)
。它不在这些函数中,因为一次赋值就足够了,但代码会在ListView的所有非自动调整大小的列上循环)

编辑

我首次尝试使用Windows主题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;
第三次尝试使用delphi的TextWidth函数

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像素的值。我还考虑了视图使用图标的可能性(在这种情况下,我会将图标的宽度加上边距再次添加到第一列)。

您可以使用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;

这对我来说很好。

这可以通过使用
DrawText
使用
DT\u CALCRECT
标志来完成,例如:谢谢,但我刚刚尝试过,使用DrawText产生的结果与使用GetTextExtent完全相同。感谢您的评论,但不幸的是,这也不起作用。考虑到内部,它使用GetTextExtentPoint32,因此与我的第一次尝试相同。不过,谢谢你。我认为它应该会起作用。我尝试过,效果很好。也许在一个新项目中尝试一下。它起作用了吗?如果是的,你的项目中一定有更改方法GetTextExtentPoint32的内容。也许你在某个地方更改了PixelsPerInch属性是吗?哦,在我的示例中,我忘了计算图标宽度。不,抱歉:我再次测试了它,但它不起作用。它做了一些事情:列的宽度几乎正确,但它仍然剪辑几乎每一个不包含超过两个单词的列。当单元格中有本地字符或特殊字符时,情况似乎更糟(法语重音字符“>”,"好吧,这可能是一个线索。如果特殊字符更多地破坏了宽度,这可能是ansi和unicode字符串之间的转换问题。可能您有ansistring,但您将其传递给接受unicode字符串的函数,或者反之亦然?当您有从a到Z的字符的简单字符串时,该方法是如何工作的?我已经尝试过了它有多种方式。首先,这是一个D6应用程序,所以所有内容都是ANSI:默认情况下,没有unicode。尽管如此,我还是尝试使用主题系统来获得适当的宽度,为了做到这一点,我必须将字符串转换为unicode字符串(在这个方向上没有问题)。结果与我从GetTextExtentPoint32获得的结果100%一致。我将添加一些代码示例来说明我的尝试。