如何最好地以编程方式判断 TLabel 的标题是否被剪裁(即使用省略号绘制)?

发布于 2024-10-05 08:03:21 字数 962 浏览 4 评论 0原文

我有一个 TLabel,其中 EllipsisPosition 设置为 epEndEllipsis,我需要能够判断文本当前是否被剪切。除了自己计算显示文本所需的面积并将其与标签的实际尺寸进行比较之外,有没有人想出一种更简单/更优雅的方法来做到这一点?

实际上,以自动防故障的方式计算所需区域似乎也不像听起来那么简单……例如,TCanvas.GetTextHeight 不考虑换行符。

TCustomLabel.DoDrawText 在内部使用 DrawTextWDrawThemeTextEx 以及 DT_CALCRECT 标志来确定是否应使用省略号与否。这里涉及到相当多的代码,所有这些代码都被声明为私有。简单地复制所有这些代码在我的书中并不完全符合“优雅”的条件...

有什么想法吗?

(我正在使用 Delphi 2010,以防有人提出特定于 Delphi 版本的解决方案)

更新 1: 我现在发现我可以简单地调用 TCustomLabel.DoDrawText( lRect, DT_CALCRECT)(仅声明为受保护),让标签执行所需的尺寸计算,而无需重复其代码。我只需确保临时将 EllipsisPosition 设置为 epNone 或完全使用临时标签实例。这实际上并没有那么糟糕,如果没有人能想到更简单的解决方案,我可能会选择它。

更新 2: 我现在已将我的解决方案添加为单独的答案。事实证明,它比我预期的要直接得多,所以可能没有更简单/更好的方法来做到这一点,但无论如何,我都会把这个问题再留一会儿,以防万一。

I have a TLabel with EllipsisPosition set to epEndEllipsis and I need to be able to tell whether the text is currently clipped or not. Besides calculating the area required to display the text myself and comparing it with the actual dimensions of the label, has anyone come up with an easier/more elegant way of doing this?

Actually, calculating the required area in a fail-safe manner also doesn't appear to be as straight forward as it sounds... For instance TCanvas.GetTextHeight does not take into account linebreaks.

TCustomLabel.DoDrawText internally uses either DrawTextW or DrawThemeTextExwith the DT_CALCRECT flag to determine whether it should use the ellipsis or not. There's quite a lot of code involved there, all of which is declared private. Simply duplicating all that code would not exactly qualify as "elegant" in my book...

Any ideas?

(I'm using Delphi 2010 in case anyone comes up with a Delphi-version-specific solution)

Update 1: I now figured out that I can simply call TCustomLabel.DoDrawText(lRect, DT_CALCRECT) directly (which is merely declared protected) to let the label perform the required size calculation without having to duplicate its code. I just have to make sure to either temporarily set EllipsisPosition to epNone or use a temporary label instance altogether. This is actually not that bad and I might just go with it if noone can think of an even simpler solution.

Update 2: I have now added my solution as a separate answer. It turned out to be rather more straight-forward than I anticipated so there probably is no easier/better way to do it but I'll leave this question open for a little while longer anyway just in case.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

萌逼全场 2024-10-12 08:03:21

FWIW,这就是我想到的(这是自定义 TLabel 后代的方法):

function TMyLabel.IsTextClipped: Boolean;
const
  EllipsisStr = '...';
var
  lEllipBup: TEllipsisPosition;
  lRect: TRect;
begin
  lRect := ClientRect;
  Dec(lRect.Right, Canvas.TextWidth(EllipsisStr));

  lEllipBup := EllipsisPosition;
  EllipsisPosition := epNone;
  try
    DoDrawText(lRect, DT_CALCRECT or IfThen(WordWrap, DT_WORDBREAK));
  finally
    EllipsisPosition := lEllipBup;
  end;
  Result := ((lRect.Right - lRect.Left) > ClientWidth)
         or ((lRect.Bottom - lRect.Top) > ClientHeight);
end;

因为它现在使用与 TCustomLabel.DoDrawText 完全相同的逻辑(尤其是人工填充和正确的 WordWrap 设置)它还可以正确处理多行和自动换行的输入文本。请注意,在这种情况下,“正确”意味着“当使用剪切标题绘制 TLabel 时,它始终返回 True,否则返回 False”。

虽然上面的代码满足了我最初的要求,但我可能不会以这种方式使用它 - 但这更多是由于 TLabel 本身的缺点:特别是对于多行文本,它通常不会这样做我本来希望它,例如,当没有足够的空间容纳多行时,第一行的最后一个单词总是会被截断,即使整行加上省略号已经安装。

FWIW, here's what I came up with (this is a method of a custom TLabel-descendant):

function TMyLabel.IsTextClipped: Boolean;
const
  EllipsisStr = '...';
var
  lEllipBup: TEllipsisPosition;
  lRect: TRect;
begin
  lRect := ClientRect;
  Dec(lRect.Right, Canvas.TextWidth(EllipsisStr));

  lEllipBup := EllipsisPosition;
  EllipsisPosition := epNone;
  try
    DoDrawText(lRect, DT_CALCRECT or IfThen(WordWrap, DT_WORDBREAK));
  finally
    EllipsisPosition := lEllipBup;
  end;
  Result := ((lRect.Right - lRect.Left) > ClientWidth)
         or ((lRect.Bottom - lRect.Top) > ClientHeight);
end;

As this now uses exactly the same logic as TCustomLabel.DoDrawText (especially the artificial padding and the correct WordWrap setting) it also deals correctly with multi-line and word-wrapped input texts. Note that "correctly" in this case means "it always returns True when the TLabel is drawn with a clipped caption and False otherwise".

While the above code does what I originally asked for I will probably not use it this way - but this is more due to shortcomings of TLabel itself: Especially with multi-line text it often does not behave the way I would have wanted it to, e.g. when there is not enough space for multiple lines, the last word of the first line will always be truncated even if that entire line plus the ellipsis would have fitted.

╰沐子 2024-10-12 08:03:21

作为起点,您可以使用

function DrawStringEllipsis(const DC: HDC; const ARect: TRect; const AStr: string): boolean;
var
  r: TRect;
  s: PChar;
begin
  r := ARect;
  GetMem(s, length(AStr)*sizeof(char) + 8);
  StrCopy(s, PChar(AStr));
  DrawText(DC, PChar(s), length(AStr), r, DT_LEFT or DT_END_ELLIPSIS or DT_MODIFYSTRING);
  result := not SameStr(AStr, s);
  FreeMem(s);
end;

示例用法:

procedure TForm1.FormClick(Sender: TObject);
begin
  Caption := 'Clipped ' + BoolToStr(DrawStringEllipsis(Canvas.Handle, Rect(10, 100, 50, 50), 'This is a text.'), true);
end;

使用此技术编写具有 WasClipped 属性的 TExtLabel 组件并不困难。事实上,TLabel 组件是 VCL 中最简单的组件之一——它只是绘制一个字符串。

As a starting point, you could use

function DrawStringEllipsis(const DC: HDC; const ARect: TRect; const AStr: string): boolean;
var
  r: TRect;
  s: PChar;
begin
  r := ARect;
  GetMem(s, length(AStr)*sizeof(char) + 8);
  StrCopy(s, PChar(AStr));
  DrawText(DC, PChar(s), length(AStr), r, DT_LEFT or DT_END_ELLIPSIS or DT_MODIFYSTRING);
  result := not SameStr(AStr, s);
  FreeMem(s);
end;

Sample usage:

procedure TForm1.FormClick(Sender: TObject);
begin
  Caption := 'Clipped ' + BoolToStr(DrawStringEllipsis(Canvas.Handle, Rect(10, 100, 50, 50), 'This is a text.'), true);
end;

It wouldn't be hard to write a TExtLabel component that has a WasClipped property using this technique. Indeed, the TLabel component is one of the simplest components in the VCL -- it just draws a string.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文