Как лучше всего программно определить, обрезана ли подпись TLabel (т.нарисовано с использованием многоточия)?

StackOverflow https://stackoverflow.com/questions/4313607

Вопрос

у меня есть TLabel с EllipsisPosition установлен в epEndEllipsis и мне нужно знать, обрезан ли текст в данный момент или нет.Помимо расчета площади, необходимой для отображения текста, и сравнения ее с фактическими размерами этикетки, придумал ли кто-нибудь более простой/более элегантный способ сделать это?

На самом деле, расчет необходимой площади безопасным способом также не так прост, как кажется...Например TCanvas.GetTextHeight не учитывает разрывы строк.

TCustomLabel.DoDrawText внутренне использует либо DrawTextW или DrawThemeTextExс DT_CALCRECT флаг, чтобы определить, следует ли использовать многоточие или нет.Там задействовано довольно много кода, весь из которого объявлен private.Простое дублирование всего этого кода в моей книге не будет считаться «элегантным»…

Есть идеи?

(Я использую Delphi 2010 на случай, если кто-нибудь предложит решение для конкретной версии Delphi)

Обновление 1: Теперь я понял, что могу просто позвонить TCustomLabel.DoDrawText(lRect, DT_CALCRECT) напрямую (что просто объявляется protected), чтобы позволить этикетке выполнить требуемый расчет размера без необходимости дублировать свой код.Мне просто нужно либо временно установить EllipsisPosition к epNone или вообще используйте экземпляр временной метки.На самом деле это не так уж и плохо, и я мог бы просто пойти на это, если никто не сможет придумать еще более простое решение.

Обновление 2: Теперь я добавил свое решение как отдельный ответ.Это оказалось более простым, чем я ожидал, поэтому, вероятно, не существует более простого/лучшего способа сделать это, но на всякий случай я все равно оставлю этот вопрос открытым еще немного.

Это было полезно?

Решение

Кстати, вот что у меня получилось (это метод кастомного 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), он также правильно обрабатывает многострочные и переносимые по словам входные тексты.Обратите внимание, что «правильно» в данном случае означает «всегда возвращает True когда TLabel рисуется с обрезанной подписью и False в противном случае".

Хотя приведенный выше код делает то, что я изначально просил, я, вероятно, не буду использовать его таким образом, но это больше связано с недостатками TLabel сам:Особенно с многострочным текстом он часто ведет себя не так, как мне хотелось бы, например.когда недостаточно места для нескольких строк, последнее слово первой строки всегда будет обрезано, даже если бы поместилась вся эта строка плюс многоточие.

Другие советы

В качестве отправной точки вы можете использовать

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;

Было бы не сложно написать TExtLabel компонент, который имеет WasClipped свойство с использованием этой техники. Действительно, TLabel Компонент является одним из самых простых компонентов в VCL - он просто рисует строку.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top