Delphi中使用ScaleBy与Margin实现高DPI窗体缩放问题
这个问题我之前也踩过坑——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
相关产品推荐
相关产品推荐

