Как создать событие изменения размера для TLabel (TGraphicControl)

Я пытаюсь создать потомка TLabel, который будет отображать подсказку со всем заголовком, когда ширина текста превышает ширину метки. Я установил для свойства EllipsisPosition значение epEndEllipsis, и мой заголовок автоматически закорачивается многоточием в конце. Все в порядке.

Однако я должен быть уведомлен, что текст был сокращен, чтобы настроить подсказку. В моем случае это может произойти только при изменении текста (сообщение CM_TEXTCHANGED) и при изменении размера компонента.

И это мой вопрос - как я могу быть уведомлен, что мой TLabel был изменен? У меня там есть якоря, поэтому они меняются вместе с формой, но я бы хотел обернуть их в отдельного потомка TLabel.

Этот код работает, но нет ли лучшего способа? Что-то вроде WM_EXITSIZEMOVE, но работает для TGraphicControl?

procedure TEllipsisLabel.WMWindowPosChanged(var Message: TWMWindowPosChanged);
begin
  inherited;

  if Assigned(Parent) then
    if Canvas.TextWidth(Caption) > Width then
      begin
        ShowHint := True;
        Hint := Caption;
      end
    else
      begin
        ShowHint := False;
        Hint := '';
      end;
end;

Большое спасибо:)

4 ответа

Решение

Я не думаю, что вы хотите получить уведомление о том, что TLabel потомок был изменен. Вместо этого вы хотите получить уведомление о сокращении текста. Я знаю, что они кажутся одинаковыми, но это не так - ярлык может быть все же шире, чем текст, он может иметь WordWrap и т.д. Кроме того, TCustomLabel потомки могут использовать несколько различных методов для рисования текста, основанных на темах / Vista / Aero glow (они сводятся к DrawThemeTextEx а также DrawText), поэтому вам нужно подключиться к этой системе, чтобы точно знать, что делает текст, который рисует, включая размер отображаемого текста.

Если вы используете не-Starter версию Delphi, посмотрите на TCustomLabel источник в stdctrls.pas, Есть два метода, представляющих интерес:

  • TCustomLabel.AdjustBounds - здесь установлен ограничивающий прямоугольник, который настраивается на перенос слов и т. д. Он делает это, вызывая (как и рисование) другой интересующий метод:
  • TCustomLabel.DoDrawText - это рисует текст и / или вычисляет ограничивающий прямоугольник текста с учетом эллипсов, обтеканий и тому подобное. Внутренне, это генерирует измененную строку, которая является текстом, который фактически нарисован. Другими словами, функциональность этого метода - это то, что говорит вам, подходит ли текст или нет.

Оба dynamic, который семантически эквивалентен virtual То есть вы можете переопределить их.

DoDrawText к сожалению, не возвращает окончательную текстовую строку, которую она рисует - если это так, вы можете переопределить ее, вызвать унаследованную версию и сравнить нарисованный текст с реальным текстом. Тем не менее, вы можете переопределить и переопределить его, и сделать это самостоятельно. Используйте код VCL в качестве руководства (вам нужна эквивалентная функциональность, хотя вам не следует копировать ее напрямую, поскольку она принадлежит Embarcadero.) dynamic, версия вашего потомка класса будет вызываться AdjustBounds, В вашем коде, когда вы укорачиваете текст, также устанавливаете флаг, он был укорочен, или немедленно генерируйте подсказку. Вуаля. Вы точно знаете точно, когда он был сокращен:)

Я не могу придумать ничего лучше, чем WM_WINDOWPOSCHANGED:

Отправляется в окно, размер, положение или место которого в Z-порядке изменились в результате вызова функции SetWindowPos или другой функции управления окнами.

Это выглядит идеально. Что ты имеешь против использования?

Вы можете просто переопределить Resize метод. Однако учтите, что ваш код

if Canvas.TextWidth(Caption) > Width then

отличается от того, как TCustomLabel.DoDrawText определяет, когда рисовать эллипсы, чтобы вы могли получить неожиданные результаты.

Кроме того, рисование эллипсов также может быть вызвано изменением шрифта, изменением настроек темы и, возможно, некоторыми другими событиями.

Я думаю, что вам нужно переопределить метод AdjustBounds. Попробуйте следующий код (просто создайте форму с TButton и TLabel на ней и замените .pas этим кодом). Этот пример демонстрирует обнаружение изменения размера метки при изменении текста. Вам нужно будет создать собственное событие, хотя.

unit unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TSizeNotifyLabel = class(TLabel)
  public
    procedure AdjustBounds; override;

end;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }

    SizeNotifyLabel: TSizeNotifyLabel;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation


procedure TSizeNotifyLabel.AdjustBounds;
begin
  inherited;
  form1.label1.caption := 'Width of Label:'+inttostr(form1.SizeNotifyLabel.Width);
end;

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  SizeNotifyLabel.Caption := SizeNotifyLabel.Caption + ' Change My Size';
end;

procedure TForm1.FormCreate(Sender: TObject);

begin
  SizeNotifyLabel := TSizeNotifyLabel.Create(self);
  with SizeNotifyLabel do begin
    caption := 'Hello World';
    left := 10;
    top := 10;
    autosize := true;
    parent := self;
  end;

end;

end.
Другие вопросы по тегам