Wednesday, January 28, 2009

Delphi: Scale your form to work in multi-resolution environment

14" and 15" monitor has became a standard desktop environment for years.  The CRT monitor became obsolete after LCD become a standard equipment now.  End user requires bigger and bigger monitor to show more from desktop application.  15" display no longer fulfill today's desktop application.  17", 19", 20", 22" and 24" and even bigger display show it's value.

The screen resolution ranges from 800x600, 1024x768, 1280x1024, 1680x1050, 1920x1080 and etc are great for users who want to show more and more but it come with a price.  You have more space on screen, but the font image may become smaller.  You get a bigger display but the resolution also increase, your application that was designed for lower resolution occupy only a small rectangle area on your nice big display.

While some application looks nicer on higher resolution display but some not.  Such small font doesn't make your priceless eyes comfort especially for aged end users.  They may think bigger display will show bigger display image for desktop application won't make their eyes pain.  This perception is not right as most desktop application design against resolution.  However, there are end users who has brilliant eye power that like to have more working space to show more on bigger display.  It would be great if we can design our application that able to adjust the form scaling for different requirements.

In Delphi, we can use Anchor and Alignment of GUI controls to make our form-based application work nicer with multi-resolution environment but the font size usually stay same for various resolutions.  This type of design doesn't change font size but the work space get increase for higher resolution display.  End users must have good eye power using this application in higher resolution display.  Although we may change font size but it is hard to align the controls properly for multi-resolution environment.

Delphi introduces other mechanism like ScaleBy, ScaleControls, PixelsPerInch for us make the form's controls scale properly in multi-resolution environment.  However, it has a drawback. It doesn't work well with anchored controls.

Scaling is an acceptable solution to solve all the above problems.  I have learned a straight and easy way to perform the scaling that even work well with anchored controls.  Changing Screen.PixelsPerInch will make all later instantiated forms display better scaled view.  The VCL source reveals that:

  1. TCustomForm.InitializeNewForm set
    FPixelsPerInch := Screen.PixelsPerInch;
  2. TScreen.PixelsPerInch is a readonly property and TScreen.FPixelsPerInch is a private field

If we can change Screen.PixelsPerInch at runtime then all subsequent form instances will get the new Screen.PixelsPerInch value.

The following code shows a possibility:

type
  TScreenHelper = class helper for TScreen
  public
    procedure SetPixelsPerInch(Value: integer);
  end;

  TScreenEx = class(TScreen)
  published
    property PixelsPerInch;
  end;

procedure TScreenHelper.SetPixelsPerInch(Value: integer);
begin
  PInteger(Integer(Self) + (Integer(GetPropInfo(TScreenEx, 'PixelsPerInch').GetProc) and $00FFFFFF))^ := Value;
end;

begin
  Screen.SetPixelsPerInch(120);

  // Create your form here ...
end.

Reference:

  1. Multi-Resolution Delphi Applications. By Zarko Gajic, About.com
  2. Hack #1: Write access to a read-only property. By Hallvard Vassbotn; Oslo, Norway

4 comments:

Dan Kloke said...

Nice, but I can't seem to get GetPropInfo to work as shown.

Tried this:
http://support.codegear.com/article/37107
which lets me use DesignEditors, but that doesn't seem to include GetPropInfo as a free-standing function.

Thanks, Dan

Chau Chee Yang said...

GetPropInfo is declared in unit TypInfo. Put the unit in your uses clause.

gb_user said...

In C++Builder we could just

*&(Screen->PixelsPerInch) = 120;

Anonymous said...

Thanks. It's a good solution!