Search code examples
delphidelphi-10-seattlehighdpivisual-inheritance

How can I get visual inheritance working at high dpi values?


I have the following example form TForm1 with two buttons (BitBtn):

base form

Additionally I have a second form TForm2 which is derived from the first form. The second button is moved to the left and another button is added:

derived form

At runtime (Windows 7), the second form looks like this:

standard font size

If I change font scaling to 125%, my form looks like this:

increased font size

Somehow, the new button has the wrong position and the wrong size. What can I do about this?

I used the following .dfm files (shortened):

object Form1: TForm1
  Left = 0
  Top = 0
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object BitBtn1: TBitBtn
    Left = 8
    Top = 8
    Width = 105
    Height = 105
    Caption = 'BitBtn1'
  end
  object BitBtn2: TBitBtn
    Left = 359
    Top = 8
    Width = 105
    Height = 105
    Caption = 'BitBtn2'
  end
end

inherited Form2: TForm2
  Caption = 'Form2'
  PixelsPerInch = 96
  TextHeight = 13
  inherited BitBtn2: TBitBtn
    Left = 191
    Top = 7
    ExplicitLeft = 191
    ExplicitTop = 7
  end
  object BitBtn3: TBitBtn
    Left = 359
    Top = 8
    Width = 105
    Height = 104
    Caption = 'BitBtn3'
  end
end

Class files are left unchanged as the designer generates them. They contain no relevant code.


Solution

  • I found a possible solution myself: one has to overwrite ReadState with the following code:

    procedure TForm2.ReadState(Reader: TReader);
    begin
      IntPtr(FReserved) := 0;
      inherited;
    end;    
    

    This looks strange. How does it work?

    I looked at the VCL source code and found that scaling after form loading is done in TForm.ReadState(...). Apparrently, a protected field FReserved of TControl is used to store the currently applied DPI value:

    • at first, all components of my base class dfm are read
    • then they are scaled and positioned correctly
    • and the currently applied DPI value is stored in FReserved
    • afterwards ReadState is called again, for the dfm file of the derived class
    • but because of the value in FReserved, scaling is not applied to components of the derived form

    So, one idea would be to reset the cached DPI value before reading the components. It scales the components of Form1 twice, but this doesn't hurt as the original sizes and positions are remembered as base value for scaling and the result is exactly the same.