Автоматическое изменение размера кнопки в Delphi
Я хочу, чтобы динамически изменить заголовок на TButton
. Проблема в том, что TButton
не изменяет размер, Если заголовок слишком длинный, чтобы поместиться на кнопке; поэтому текст кровоточит по краям кнопки.
как я могу заставить кнопку Изменить размер, чтобы соответствовать заголовку?
некоторые идеи:
- используйте другой компонент кнопки, который может изменять размер. Есть такой?
- подкласс
TButton
и setAutoSize=True
(не пробовали это, не знаю, сработает ли это). - вычислите размер заголовка в пикселях и вручную измените ширину каждый раз, когда я изменяю заголовок.
2 ответов
подкласс TButton
сделайте уже присутствует AutoSize
свойство public и реализация CanAutoSize
:
type
TButton = class(StdCtrls.TButton)
private
procedure CMFontchanged(var Message: TMessage); message CM_FONTCHANGED;
procedure CMTextchanged(var Message: TMessage); message CM_TEXTCHANGED;
protected
function CanAutoSize(var NewWidth, NewHeight: Integer): Boolean; override;
public
property AutoSize;
end;
function TButton.CanAutoSize(var NewWidth, NewHeight: Integer): Boolean;
const
WordBreak: array[Boolean] of Cardinal = (0, DT_WORDBREAK);
var
DC: HDC;
R: TRect;
SaveFont: HFONT;
DrawFlags: Cardinal;
begin
DC := GetDC(Handle);
try
SetRect(R, 0, 0, NewWidth - 8, NewHeight - 8);
SaveFont := SelectObject(DC, Font.Handle);
DrawFlags := DT_LEFT or DT_CALCRECT or WordBreak[WordWrap];
DrawText(DC, PChar(Caption), Length(Caption), R, DrawFlags);
SelectObject(DC, SaveFont);
NewWidth := R.Right + 8;
NewHeight := R.Bottom + 8;
finally
ReleaseDC(Handle, DC);
end;
Result := True;
end;
procedure TButton.CMFontchanged(var Message: TMessage);
begin
inherited;
AdjustSize;
end;
procedure TButton.CMTextchanged(var Message: TMessage);
begin
inherited;
AdjustSize;
end;
обновление:
в адрес Дэвид комментария о том, почему жестко закодированные 8 пикселей: проще говоря, это выглядит просто отлично. Но я сделал небольшое визуальное исследование ширины границ кнопок:
Button state Windows XP Windows 7
Classic Themed Classic Themed
Focused, incl. focus rect 5 4 5 3
Focused, excl. focus rect 3 4 3 3
Not focused 2 2 2 2
Disabled 2 1 2 2
чтобы учесть операционную систему, см. получение версии Windows. Тематизация может быть учтена оценка Themes.ThemeServices.ThemesEnabled
. Если true, содержимое rect, зарезервированное для текста, можно получить с помощью GetThemeBackgroundContentRect
который обернут в ThemeServices
переменной:
uses
Themes;
var
DC: HDC;
Button: TThemedButton;
Details: TThemedElementDetails;
R: TRect;
begin
DC := GetDC(Button2.Handle);
try
SetRect(R, 0, 0, Button2.Width, Button2.Height);
Memo1.Lines.Add(IntToStr(R.Right - R.Left));
Button := tbPushButtonNormal;
Details := ThemeServices.GetElementDetails(Button);
R := ThemeServices.ContentRect(DC, Details, R);
повторение моего теста с помощью этой процедуры показывает постоянный размер границы 3 пикселя в любой версии и с любым состоянием кнопки. Таким образом, 8 пикселей общего поля оставляет 1 пиксель передышки для текста.
и чтобы учесть размер шрифта, я предлагаю следующее изменить:
function TButton.CanAutoSize(var NewWidth, NewHeight: Integer): Boolean;
const
WordBreak: array[Boolean] of Cardinal = (0, DT_WORDBREAK);
var
DC: HDC;
Margin: Integer;
R: TRect;
SaveFont: HFONT;
DrawFlags: Cardinal;
begin
DC := GetDC(Handle);
try
Margin := 8 + Abs(Font.Height) div 5;
SetRect(R, 0, 0, NewWidth - Margin, NewHeight - Margin);
SaveFont := SelectObject(DC, Font.Handle);
DrawFlags := DT_LEFT or DT_CALCRECT or WordBreak[WordWrap];
DrawText(DC, PChar(Caption), -1, R, DrawFlags);
SelectObject(DC, SaveFont);
NewWidth := R.Right + Margin;
NewHeight := R.Bottom + Margin;
finally
ReleaseDC(Handle, DC);
end;
Result := True;
end;
и я должен быть честным: она выглядит лучше.
Я закончил с опцией 3 ("вычислить размер заголовка в пикселях и вручную изменить ширину каждый раз, когда я изменяю заголовок")
мой код выглядит примерно так:
// Called from the form containing the button
button.Caption := newCaption;
button.Width := self.Canvas.TextWidth(newCaption);