如何处理Windows 11中引入的TForm和自定义伸缩?

r1zhe5dt  于 2022-09-21  发布在  Windows
关注(0)|答案(1)|浏览(136)

Windows 11引入了一个新的选项,允许定制屏幕比例:

应用自定义缩放时,读取和写入窗体宽度和高度的行为不佳。

特别是,在我的Delphi应用程序中,每次关闭窗体时,我都会将窗体的宽度和高度存储在INI文件中,以便在下次显示窗体时恢复它们。

// store to file code
IniFile.WriteInteger('FORM','Width',AForm.Width);
IniFile.WriteInteger('FORM','Top',AForm.Top);

// restore from file code
  StoredHeight:= IniFile.readInteger('FORM','Height',AForm.Height);
  StoredWidth := IniFile.readInteger('FORM','Width',AForm.Width);

if StoredWidth >= Screen.Width then
  AForm.Width := Screen.Width
else
  begin
    AForm.Width := StoredWidth;
    if StoredLeft < 0 then
    AForm.left  := 0
    else
      AForm.left := StoredLeft;
  end;
  if StoredHeight >= Screen.Height then
  AForm.Height := Screen.Height
  else
    begin
      AForm.Height :=  StoredHeight;
      if StoredTop < 0 then
      AForm.Top := 0
      else
        AForm.Top := StoredTop;
    end;

问题是,当TForm.HeightTForm.Width存储到文件中时,它们将按自定义缩放百分比(或至少增加到接近该百分比的数字)增加,而当设置这些值时,它们将被正确应用。因此,每次显示表单时,它都会变得越来越大。

我必须处理缩放问题并且必须对其进行补偿(通过将值除以缩放系数),这正常吗?我希望这对我来说是透明的。

此外,有人知道如何从Windows API中检索自定义缩放设置吗?我在谷歌上搜索了一下,但没有成功。

谢谢!

ma8fv8wu

ma8fv8wu1#

在Delphi 11.0 Alexandria中有一个改进。IDE现在完全支持高分辨率屏幕上的高DPI缩放。这种高DPI支持包括在代码编辑器中的支持,以及在设计窗体时VCL和FMX的支持。VCL Form Designer中有一个控制缩放的设置,因此您可以将其默认设置为未缩放。

您必须编译您的应用程序以支持高DPI(‘DPI感知’)。如果应用程序**‘不知道DPI’,Windows会将其扩展,但扩展会增加模糊性。如果应用程序是‘DPI感知’**,那么应用到其窗口的缩放就会清晰得多。

您可以将VCL Form Designer缩放到任何DPI(任何比例)。这是使用与VCL在运行时进行自我伸缩时使用的相同伸缩技术来完成的。**此设置位于工具>选项>用户界面>表单设计器>高DPI中。**更改后,需要关闭并重新打开表单设计器才能生效。

默认情况下,打开表单时,表单设计为96DPI,即100%。一个关键的知识是,当一个窗体被缩放时,Left、Height等属性都会改变。这与你运行应用程序时的情况完全相同,它会被缩放;这些值会乘以屏幕比例。

Windows(以及VCL)使用整数坐标表示其大小和位置。这意味着任何缩放可能并不总是精确的。在实践中,只需扩展一次就可以了(例如,当应用程序运行时,它从设计时的低分辨率坐标向上扩展)。它还可以进行几次微调,比如在发布后从一个显示器切换到另一个显示器。当规模扩大多次时,这一点更为重要。因此,在任何比例下以高DPI进行设计是可以的,甚至可以在较低的比例下运行-VCL将正确地缩放您的应用程序-但重要的是避免反复缩放,如果每次在设计器中打开一个表单时,它是以不同的DPI打开的,那么就会发生这种情况。

您可以在以下Embarcadero博客中阅读有关DPI扩展的更多信息:https://blogs.embarcadero.com/new-in-rad-studio-11-high-dpi-ide-and-form-designing/

首先要尝试的是禁用应用程序的每个窗体的Scaled属性(您可以在窗体的对象检查器上找到该属性),然后测试宽度和高度值是否正确写入。如果这不能解决您的问题,那么我建议您采取一些变通办法。

以下是解决方法,直到Embarcadero修复此问题。

您可以在关闭表单时保存宽度和高度值,然后根据最终用户设置的dpi对它们进行规格化(使用MulDiv函数)。

代码应该是这样的:

procedure AForm.FormClose(Sender: TObject; var Action: TCloseAction);
var
  IniFile: TIniFile;
begin
  IniFile := TIniFile.Create( ChangeFileExt( Application.ExeName, '.ini' ) );
  try

    IniFile.WriteInteger('FORM','Width',MulDiv(AForm.Width, Screen.PixelsPerInch, PixelsPerInch));
    IniFile.WriteInteger('FORM','Height',MulDiv(AForm.Height, Screen.PixelsPerInch, PixelsPerInch))
  finally
    IniFile.Free;
  end;

end;

不过,您可以尝试使用Monitor.PixelsPerInch而不是Screen.PixelsPerInch。这样,它将适用于在不同显示器上应用不同的自定义缩放比例的情况。

另一个解决办法是从注册表中读取自定义缩放属性,并在规格化后保存宽度和高度值。自定义缩放属性保存在以下注册表项中:HKEY_CURRENT_USER\Control Panel\DesktopLogPixels项中。此注册表项控制Windows 10和11中的DPI缩放级别。

LogPixels = 96,  means 100% scaling
LogPixels = 120, means 125% scaling
LogPixels = 144, means 150% scaling
LogPixels = 192, means 200% scaling etc.

因此,解决方法是读取此注册表项,并缩小(或放大)要写入ini文件的宽度和高度。

代码应该是这样的:

procedure AForm.FormClose(Sender: TObject; var Action: TCloseAction);
var
  IniFile: TIniFile;
  RegKey: TRegistry;
  intScalingSize: Integer;   //Here I will save the scaling size stored in the registry. (eg. 96, 144 etc)
  reaScalingSize: Real;      //Here I will save the scaling size percentage (eg. for 96 I will store 1.0, for 144 I will store 1.5 etc)
  intFormUnscaledWidth, intFormUnscaledHeight: Integer;  //Here I will store the unscaled width and height.
begin
  IniFile := TIniFile.Create( ChangeFileExt( Application.ExeName, '.ini' ) );
  RegKey := TRegistry.Create;
  intScalingSize := 0;
  try
    RegKey.RootKey := HKEY_CURRENT_USER;
    if RegKey.OpenKey('Control PanelDesktop', False) then
      begin
        intScalingSize := RegKey.ReadInteger('LogPixels');
        RegKey.CloseKey;
      end
      else
        intScalingSize := 96;
    // LogPixels = 96,  means 100% scaling
    // LogPixels = 120, means 125% scaling
    // LogPixels = 144, means 150% scaling
    // LogPixels = 192, means 200% scaling
    //Use linear interpolation to map scaling to percentage
    reaScalingSize := 100 + ((100/96)*(intScalingSize - 96));

    intFormUnscaledWidth := Round(AForm.Width / reaScalingSize);
    intFormUnscaledHeight := Round(AForm.Height / reaScalingSize);

    IniFile.WriteInteger('FORM','Width',intFormUnscaledWidth);
    IniFile.WriteInteger('FORM','Height',intFormUnscaledHeight);
  finally
    RegKey.Free;
    IniFile.Free;
  end;

在Embarcadero解决此问题之前,我希望此解决方法将对您有所帮助。

相关问题