Как создать событие изменения размера для 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;

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


person Community    schedule 10.03.2011    source источник
comment
Может переопределить SetBounds, где выполняется WM_WINDOWPOSCHANGED. Или, если вы хотите поместить код в обработчик событий, вы можете просто опубликовать свойство OnResize своего потомка. Не вижу причин, по которым любой из них был бы лучше, чем обработчик сообщений ...   -  person Sertac Akyuz    schedule 10.03.2011


Ответы (4)


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

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

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

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

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

person David    schedule 10.03.2011
comment
Да, именно так, мне нужно получать уведомление, когда мой TLabel сокращается трехточечной строкой epEndEllipsis, но как это сделать? - person ; 11.03.2011
comment
Последний абзац моего ответа должен объяснить это (в частности, переопределить и переопределить его ... когда вы сокращаете текст, также устанавливайте флаг, он был сокращен или ...). Что в нем ты не понимаешь / могу ли я расширить для тебя? - person David; 11.03.2011

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

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

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

person David Heffernan    schedule 10.03.2011
comment
Тот факт, что он вызывается слишком рано (до того, как родительский элемент известен), и мне нужно перенести родительское назначение, иначе я получаю нарушение доступа, поэтому я просил какое-то конкретное сообщение или метод, который не нуждается в этой проверке и который является призвал только для изменения размера. - person ; 10.03.2011
comment
Разве вы не можете выполнять только ту работу, которая требует назначения Родителя, когда Родитель назначен? - person David Heffernan; 10.03.2011
comment
Да, но я надеялся, что есть что-то вроде WM_EXITSIZEMOVE, которое может быть вызвано только один раз, когда элемент управления завершит определение размера. - person ; 10.03.2011
comment
Разве он не хочет проверять, когда текст укорачивается, а не когда изменяется размер метки? Изменение размера может вообще не изменить отображаемый текст. - person David; 11.03.2011
comment
@David M - да, знает :) но как бы вы это сделали? Я хочу отображать подсказку со всей подписью, когда текст укорачивается, вот и все - person ; 11.03.2011

Я думаю, вам нужно переопределить метод 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.
person M Schenkel    schedule 10.03.2011
comment
Я не думаю, что OP использует AutoSize. Если да, то зачем нам многоточие? - person David Heffernan; 10.03.2011
comment
@David - вы правы, но в любом случае AdjustBounds тоже может быть решением. - person ; 10.03.2011

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

if Canvas.TextWidth(Caption) > Width then

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

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

person Ondrej Kelle    schedule 10.03.2011
comment
Я согласен с вами, но в моем случае достаточно рассчитать ширину таким образом, потому что шрифты не зависят от темы (надеюсь :) и на этикетке нет рамок. И упомянутый метод Resize мне нравится - как можно скорее проверю. И изменение шрифта у меня было в голове, но этого никогда не произойдет, для меня важно только изменение текста и изменение размера :) - person ; 10.03.2011