https://www.cnblogs.com/zhangzhifeng/category/835602.html

这是个困惑我很长时间的问题,到今天终于得到解决了。

话说Delphi有个很强的窗体设计器,这一点让VC粉丝垂涎三尺而不可得。但是,Delphi里设计的窗体并没有自动适应屏幕分辨率的属性,也就是说,软件设计时调整完美的窗体控件布局,在不同屏幕分辨率的机器上运行时可能会变得面目全非。控件之间会相互移位,有的甚至移出窗体再也找不到了。

这个问题在网上搜索过多次,但大都依据控件方法ScaleBy或者ChangeScale。采用这两个方法进行自适应调整,我自己都试过,但效果并不理想。后来我自己也写了一个继承自窗体的基类,覆盖构造函数,调用自己的一个设备分辨率自适应方法,该方法遍历窗体上所有控件,并按照设计时的屏幕分辨率和当前屏幕分辨率的比值,逐一计算控件的位置和尺寸。这个想法是不错,效果也是有的,比单纯的采用ScaleBy或者ChangeScale方法要好,但也不是非常理想,没有达到自己设想的要求。原因在哪里,一直不知道。

我原来的代码曾经发布在Delphi盒子和CSDN上。

这个问题今天终于得以彻底解决了!!

原因是,我原以为将所有控件的Align属性设为alnone,Anchors属性设为空[],控件位置和尺寸就不会受其容器尺寸改变的影响。今天我在设计期对此进行试验时,发现不是这样。当窗体大小改变的时候,即使某个控件的Align:=alNone,Anchors:=[],它依然会随着窗体尺度的变化而变化。这意味着我需要一个数组事先保存所有控件的原始位置和尺寸。在窗体因为屏幕分辨率的改变而自动调整时,计算的依据依然是不变的原始窗体位置尺寸数据,这样问题就解决了。

闲话少说,上源码。

unit uMyClassHelpers;

interface

Uses

SysUtils,Windows,Classes,Graphics, Controls,Forms,Dialogs,
  uMySysUtils;

Const   //记录设计时的屏幕分辨率

OriWidth=1366;
  OriHeight=768;

Type

TfmForm=Class(TForm)   //实现窗体屏幕分辨率的自动调整
  Private
    fScrResolutionRateW: Double;
    fScrResolutionRateH: Double;
    fIsFitDeviceDone: Boolean;
    fPosition:Array of TRect;
    procedure FitDeviceResolution;
  Protected
    Property IsFitDeviceDone:Boolean Read fIsFitDeviceDone;
    Property ScrResolutionRateH:Double Read fScrResolutionRateH;
    Property ScrResolutionRateW:Double Read fScrResolutionRateW;
  Public
    Constructor Create(AOwner: TComponent); Override;
  End;

TfdForm=Class(TfmForm)   //增加对话框窗体的修改确认
  Protected
    fIsDlgChange:Boolean;
  Public
  Constructor Create(AOwner: TComponent); Override;
  Property IsDlgChange:Boolean Read fIsDlgChange default false;
 End;

implementation

Constructor TfmForm.Create(AOwner: TComponent);
begin
 Inherited Create(AOwner);
  fScrResolutionRateH:=1;
  fScrResolutionRateW:=1;
  Try
    if Not fIsFitDeviceDone then
    Begin
      FitDeviceResolution;
   fIsFitDeviceDone:=True;
    End;
  Except
  fIsFitDeviceDone:=False;
  End;
end;

procedure TfmForm.FitDeviceResolution;
Var
  i:Integer;
  LocList:TList;
  LocFontSize:Integer;
  LocFont:TFont;
  LocCmp:TComponent;
  LocFontRate:Double;
  LocRect:TRect;
  LocCtl:TControl;
begin
  LocList:=TList.Create;
  Try
    Try
      if (Screen.width<>OriWidth)OR(Screen.Height<>OriHeight) then
      begin
        Self.Scaled:=False;
        fScrResolutionRateH:=screen.height/OriHeight;
        fScrResolutionRateW:=screen.Width/OriWidth;
        Try
          if fScrResolutionRateH<fScrResolutionRateW then
            LocFontRate:=fScrResolutionRateH
          Else
            LocFontRate:=fScrResolutionRateW;
        Finally
          ReleaseDC(0, GetDc(0));
        End;

For i:=Self.ComponentCount-1 Downto 0 Do
        Begin
          LocCmp:=Self.Components[i];
          If LocCmp Is TControl Then
            LocList.Add(LocCmp);
          If PropertyExists(LocCmp,'FONT') Then
          Begin
            LocFont:=TFont(GetObjectProperty(LocCmp,'FONT'));
            LocFontSize := Round(LocFontRate*LocFont.Size);
            LocFont.Size:=LocFontSize;
          End;
        End;

SetLength(fPosition,LocList.Count+1);
        For i:=0 to LocList.Count-1 Do
          With TControl(LocList.Items[i])Do
            fPosition[i+1]:=BoundsRect;
        fPosition[0]:=Self.BoundsRect;

With LocRect Do
        begin
           Left:=Round(fPosition[0].Left*fScrResolutionRateW);
           Right:=Round(fPosition[0].Right*fScrResolutionRateW);
           Top:=Round(fPosition[0].Top*fScrResolutionRateH);
           Bottom:=Round(fPosition[0].Bottom*fScrResolutionRateH);
           Self.SetBounds(Left,Top,Right-Left,Bottom-Top);
        end;

i:= LocList.Count-1;
        While (i>=0) Do
         Begin
          LocCtl:=TControl(LocList.Items[i]);
          If LocCtl.Align=alClient Then
          begin
            Dec(i);
            Continue;
          end;
          With LocRect Do
          begin
             Left:=Round(fPosition[i+1].Left*fScrResolutionRateW);
             Right:=Round(fPosition[i+1].Right*fScrResolutionRateW);
             Top:=Round(fPosition[i+1].Top*fScrResolutionRateH);
             Bottom:=Round(fPosition[i+1].Bottom*fScrResolutionRateH);
             LocCtl.SetBounds(Left,Top,Right-Left,Bottom-Top);
          end;
          Dec(i);
        End;
      End;

Except on E:Exception Do
      Raise Exception.Create('进行屏幕分辨率自适应调整时出现错误'+E.Message);
    End;
  Finally
    LocList.Free;
  End;
end;

{ TfdForm }

constructor TfdForm.Create(AOwner: TComponent);
begin
  inherited;
  fIsDlgChange:=False;
end;

end.

上面包括两个类,一个是普通窗体类,一个是其子类对话框型窗体类。在实际应用过程中只要自己创建的窗体类继承自以上两个类中的一个,例如 TForm1 = class(TfdForm),则不需添加任何源码,设计出窗体会自动调整其上控件的尺寸,以适应不同的屏幕分辨率。

以上源码经过验证,效果非常好,解决了一个多年未决的问题!

unit uMyClassHelpers;
{实现窗体自适应调整尺寸以适应不同屏幕分辩率的显示问题。
使用说明:
但你自己可以随便就做一个例子。
新建一个窗体,把新窗体继承的类TForm改成TfmForm或者TfdForm,
然后随便拖一些控件在窗体,改变OriWidth和OriHeight的值来模拟设计时屏幕分辨率,
或者改变自己电脑的屏幕分辨率来模拟实际情况,就可以很方便地演示窗体的自适应变化。
整个过程不需要手工添加一条源码。
} interface
uses
SysUtils, Windows, Classes, Graphics, Controls, Forms, Dialogs, Math,
TypInfo; const //记录设计时的屏幕分辨率
OriWidth = 1920;
OriHeight = 1080; type
TfmForm = class(TForm) //实现窗体屏幕分辨率的自动调整
private
fScrResolutionRateW: Double;
fScrResolutionRateH: Double;
fIsFitDeviceDone: Boolean;
procedure FitDeviceResolution;
protected
property IsFitDeviceDone: Boolean read fIsFitDeviceDone;
property ScrResolutionRateH: Double read fScrResolutionRateH;
property ScrResolutionRateW: Double read fScrResolutionRateW;
public
constructor Create(AOwner: TComponent); override;
end; TfdForm = class(TfmForm) //增加对话框窗体的修改确认
protected
fIsDlgChange: Boolean;
public
constructor Create(AOwner: TComponent); override;
property IsDlgChange: Boolean read fIsDlgChange default false;
end; implementation function PropertyExists(const AObject: TObject; const APropName: string): Boolean;
//判断一个属性是否存在
var
PropInfo: PPropInfo;
begin
PropInfo := GetPropInfo(AObject.ClassInfo, APropName);
Result := Assigned(PropInfo);
end; function GetObjectProperty(
const AObject: TObject;
const APropName: string
): TObject;
var
PropInfo: PPropInfo;
begin
Result := nil;
PropInfo := GetPropInfo(AObject.ClassInfo, APropName);
if Assigned(PropInfo) and
(PropInfo^.PropType^.Kind = tkClass) then
Result := GetObjectProp(AObject, PropInfo);
end; constructor TfmForm.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
fScrResolutionRateH := 1;
fScrResolutionRateW := 1;
try
if not fIsFitDeviceDone then
begin
FitDeviceResolution;
fIsFitDeviceDone := True;
end;
except
fIsFitDeviceDone := False;
end;
end; procedure TfmForm.FitDeviceResolution;
var
LocList: TList;
LocFontRate: Double;
LocFontSize: Integer;
LocFont: TFont;
locK: Integer;
{计算尺度调整的基本参数}
procedure CalBasicScalePars;
begin
try
Self.Scaled := False;
fScrResolutionRateH := screen.height / OriHeight;
fScrResolutionRateW := screen.Width / OriWidth;
LocFontRate := Min(fScrResolutionRateH, fScrResolutionRateW);
except
raise;
end;
end; {保存原有坐标位置:利用递归法遍历各级容器里的控件,直到最后一级}
procedure ControlsPostoList(vCtl: TControl; vList: TList);
var
locPRect: ^TRect;
i: Integer;
locCtl: TControl;
locFontp: ^Integer;
begin
try
New(locPRect);
locPRect^ := vCtl.BoundsRect;
vList.Add(locPRect);
if PropertyExists(vCtl, 'FONT') then
begin
LocFont := TFont(GetObjectProperty(vCtl, 'FONT'));
New(locFontp);
locFontP^ := LocFont.Size;
vList.Add(locFontP);
// ShowMessage(vCtl.Name+'Ori:='+InttoStr(LocFont.Size));
end;
if vCtl is TWinControl then
for i := 0 to TWinControl(vCtl).ControlCount - 1 do
begin
locCtl := TWinControl(vCtl).Controls[i];
ControlsPosToList(locCtl, vList);
end;
except
raise;
end;
end; {计算新的坐标位置:利用递归法遍历各级容器里的控件,直到最后一层。
计算坐标时先计算顶级容器级的,然后逐级递进}
procedure AdjustControlsScale(vCtl: TControl; vList: TList; var vK: Integer);
var
locOriRect, LocNewRect: TRect;
i: Integer;
locCtl: TControl;
begin
try
if vCtl.Align <> alClient then
begin
locOriRect := TRect(vList.Items[vK]^);
with locNewRect do
begin
Left := Round(locOriRect.Left * fScrResolutionRateW);
Right := Round(locOriRect.Right * fScrResolutionRateW);
Top := Round(locOriRect.Top * fScrResolutionRateH);
Bottom := Round(locOriRect.Bottom * fScrResolutionRateH);
vCtl.SetBounds(Left, Top, Right - Left, Bottom - Top);
end;
end;
if PropertyExists(vCtl, 'FONT') then
begin
Inc(vK);
LocFont := TFont(GetObjectProperty(vCtl, 'FONT'));
locFontSize := Integer(vList.Items[vK]^);
LocFont.Size := Round(LocFontRate * locFontSize);
// ShowMessage(vCtl.Name+'New:='+InttoStr(LocFont.Size));
end;
Inc(vK);
if vCtl is TWinControl then
for i := 0 to TwinControl(vCtl).ControlCount - 1 do
begin
locCtl := TWinControl(vCtl).Controls[i];
AdjustControlsScale(locCtl, vList, vK);
end;
except
raise;
end;
end; {释放坐标位置指针和列表对象}
procedure FreeListItem(vList: TList);
var
i: Integer;
begin
for i := 0 to vList.Count - 1 do
Dispose(vList.Items[i]);
vList.Free;
end; begin
LocList := TList.Create;
try
try
if (Screen.width <> OriWidth) or (Screen.Height <> OriHeight) then
begin
CalBasicScalePars;
// AdjustComponentFont(Self);
ControlsPostoList(Self, locList);
locK := 0;
AdjustControlsScale(Self, locList, locK); end;
except on E: Exception do
raise Exception.Create('进行屏幕分辨率自适应调整时出现错误' + E.Message);
end;
finally
FreeListItem(locList);
end;
end; { TfdForm } constructor TfdForm.Create(AOwner: TComponent);
begin
inherited;
fIsDlgChange := False;
end; end.

  

Delphi自动适应屏幕分辨率的属性的更多相关文章

  1. delphi 窗体自适应屏幕分辨率

    delphi 窗体自适应屏幕分辨率 这是个困惑我很长时间的问题,到今天终于得到解决了. 话说Delphi有个很强的窗体设计器,这一点让VC粉丝垂涎三尺而不可得.但是,Delphi里设计的窗体并没有自动 ...

  2. delphi 动态更改屏幕分辨率(转)

    一.如何动态更改屏幕分辨率 有许多小工具可以在不重新启动Windows的条件下,动态更改屏幕分辨率.你是不是也想自己动手做一个呢?请在interface段中加入下面一句 function Resolu ...

  3. RS报表自动适应屏幕分辨率大小

    问题:同一个报表,由于用户电脑显示器大小,分辨率大小不同显示的不一样,看起来不完整或者很不协调 原因:设计报表大小属性的时候使用了固定值属性,比如限制为宽:1200px 那么在电脑屏幕小分辨率很小的情 ...

  4. Silverlight自动根据屏幕分辨率进行布局

    xaml: <UserControl x:Class="SLCenterLayout.MainPage" xmlns="http://schemas.microso ...

  5. Delphi:窗体自适应屏幕分辨率(根据预设值的比例改变)

    delphi 程序适应屏幕分辨率,先在表单单元的Interface部分定义两个常量, 表示设计时的屏幕的宽度和高度(以像素为单位). 在表单的Create事件中先判断 当前分辨率是否与设计分辨率相同, ...

  6. Delphi:窗体自适应屏幕分辨率的改进

    在窗体依据屏幕分辨率自适应调整尺度方面,昨天的工作可以说是一个突破点.昨天的工作找到了长期以来我的原有方案的问题所在,这是非常关键的.但是昨天晚上的解决方案并不完美,今天的这个才是比较完美的解决版. ...

  7. CSS根据屏幕分辨率宽度自动适应的办法

    CSS根据屏幕分辨率宽度自动适应的办法 第一种办法是js选择CSS <SCRIPT language=JavaScript><!-- Beginif (screen.width == ...

  8. Unity3D NGUI自适应屏幕分辨率(2014/4/17更新)

    原地址:http://blog.csdn.net/asd237241291/article/details/8126619 原创文章如需转载请注明:转载自 脱莫柔Unity3D学习之旅 本文链接地址: ...

  9. WPF 获取屏幕分辨率(获取最大宽高)等

    double x = SystemParameters.WorkArea.Width;//得到屏幕工作区域宽度 double y = SystemParameters.WorkArea.Height; ...

随机推荐

  1. hive vs hbase

    HIVE和HBASE区别 两者分别是什么? Apache Hive是一个构建在Hadoop基础设施之上的数据仓库.通过Hive可以使用HQL语言查询存放在HDFS上的数据.HQL是一种类SQL语言,这 ...

  2. cdh-5.10.0搭建安装

    1.修改主机名为master, slave1, slave2 vim /etc/sysconfig/network HOSTNAME = master HOSTNAME = slave1 HOSTNA ...

  3. (cx_Oracle.DatabaseError) DPI-1047: 64-bit Oracle Client library cannot be loaded: "libclntsh.so: cannot open shared object file: No such file or directory"

    打开https://oracle.github.io/odpi/doc/installation.html 官方相关如下 Oracle Instant Client RPM¶ To run ODPI- ...

  4. 后台拿webshell方法(2)

    在这里总结一下后台拿webshell的方法: 备份突破(在博客上有随笔介绍) 一般在后台上传文件类型受到限制,可以利用数据库备份将其上传的格式改为后门执行格式即可: asp更改为jpg     jpg ...

  5. dubbo服务使用spring-data-mongodb进行时间查询的bug记录

    一.项目情况:spring-boot+mongodb+dubbo. 二.问题:调用dubbo服务并使用spring-data-mongodb的gte,lte时间段比较查询, @Reference(re ...

  6. MacOs -bash: warning: setlocale: LC_CTYPE: cannot change locale (UTF-8): No such file or directory

    1解决iterm远程登录主机报错 -bash: warning: setlocale: LC_CTYPE: cannot change locale (UTF-8): No such file or ...

  7. typedef 用法总结

    原文转自:http://www.cnblogs.com/ggjucheng/archive/2011/12/27/2303238.html 引言 typedef 声明,简称 typedef,为现有类型 ...

  8. CodeForces Contest #1137: Round #545 (Div. 1)

    比赛传送门:CF #1137. 比赛记录:点我. 每次都自闭的 div1 啊,什么时候才能上 IM 呢. [A]Skyscrapers 题意简述: 有一个 \(n\times m\) 的矩阵 \(a_ ...

  9. STM32F103X datasheet学习笔记---USART

    1.前言 通用同步异步收发器(USART)提供了一种灵活的方法与使用工业标准NRZ异步串行数据格式的外部设备之间进行全双工数据交换. USART利用分数波特率发生器提供宽范围的波特率选择. 它支持同步 ...

  10. 【Connection Events】【BLE】【原创】

    Connection Events  本人在TI官网的学习笔记,现整理如下   两台BLE设备建立连接后,所有的通信事件都是通过Connection Events中发生的           上图为两 ...