You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi中使用ScaleBy与Margin实现高DPI窗体缩放问题

解决Delphi高DPI缩放时TSpeedButton Margin不跟随缩放的问题

这个问题我之前也踩过坑——Delphi的自动/手动缩放机制默认不会处理TSpeedButton.Margin这个属性,导致原本对齐的TLabel和TSpeedButton文本在高DPI下偏移,确实挺闹心的。下面给你两种场景下的具体解决方案:

一、自动缩放(Scaled := True)场景

Delphi的自动缩放只会处理控件的位置、尺寸这类"显性"属性,Margin属于控件内部的布局参数,不在默认缩放列表里。解决办法是自定义一个继承自TSpeedButton的控件,重写它的缩放逻辑,让Margin跟着DPI比例走:

type
  TScaledSpeedButton = class(TSpeedButton)
  protected
    procedure ScaleControls(M, D: Integer); override;
  end;

procedure TScaledSpeedButton.ScaleControls(M, D: Integer);
begin
  inherited; // 先执行默认的缩放逻辑
  // 按DPI缩放比例调整Margin(M/D就是当前缩放比例)
  Margin := MulDiv(Margin, M, D);
end;

把原来的TSpeedButton替换成这个自定义控件,窗体自动缩放时,Margin就会和其他属性一起按比例调整,TLabel和它的文本对齐就能保持一致了。

二、手动缩放(Scaled := False + ScaleBy)场景

如果是手动调用ScaleBy来缩放窗体,同样需要手动遍历所有TSpeedButton,同步调整它们的Margin值。比如写一个这样的辅助方法:

procedure TYourForm.ApplySpeedButtonMarginScaling(const BaseDPI: Integer = 96);
var
  Comp: TComponent;
  SpeedBtn: TSpeedButton;
  ScaleFactor: Double;
begin
  ScaleFactor := Screen.PixelsPerInch / BaseDPI;
  // 遍历窗体上所有TSpeedButton
  for Comp in Components do
  begin
    if Comp is TSpeedButton then
    begin
      SpeedBtn := TSpeedButton(Comp);
      SpeedBtn.Margin := Round(SpeedBtn.Margin * ScaleFactor);
    end;
  end;
end;

在你调用ScaleBy之后,紧接着调用这个方法就行。比如:

// 假设要缩放到当前系统DPI
ScaleBy(Round(Screen.PixelsPerInch / 96 * 100), 100);
ApplySpeedButtonMarginScaling;

额外小提示

TLabel的文本对齐是基于自身控件边界的,自动缩放时它的位置会被正确调整,所以只要TSpeedButton的Margin按比例缩放,两者的文本起始像素就能保持和96dpi下一致的效果。如果你的布局里有很多TSpeedButton,自定义控件的方式会更省心,不用每次手动遍历调整。

内容的提问来源于stack exchange,提问作者Jens

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 08:39:41