High DPI display and 4K Monitors are getting more common these days. Supporting higher settings than 96 dpi is already possible with some of the newer Delphi versions.
Nevertheless this is still a time and resource consuming task. Not only do you need additional sizes for the icons used in your application, you also have to check each form and frame for looking good in higher scaled environments.
A couple of Delphi applications, if not even most, consist of several forms – often there are hundreds of them. This number alone establishes a lower bound for the time required doing checks and tweaks when necessary. Especially in teams with a short release cycle (say 2 – 4 weeks) it means that this may have to be done in a long running feature branch. As a significant part of the work is fiddling around with the form files (dfm), often accompanied by restructuring the controls, this most likely leads to hard to resolve merge conflicts when the main development also tackles these forms.
There are several ways to solve this dilemma. The simplest one is to handle one form or groups of forms sequentially and release each change within the normal release cycle. The application is then kept non DPI aware until all forms are done. This should be accompanied by special builds with high DPI active given to testers only to avoid a nearly blind-folded development. Problems will arise and it is far better to find them early.
A drawback of this approach is that the testers will see all the non-handled forms probably containing high DPI artefacts, which may even render the form unusable. At best this may produce a lot of superfluous bug reports, while at worse it can demotivate the testers.
The better approach is to have all of these later-to-be-handled forms displayed in a non DPI aware way until it is the forms turn to be done properly. For this we need something like a mixed DPI VCL application. Fortunately such thing is possible – at least in systems running Windows 10 version 1607 or higher.
For a practical demonstration we fire up Delphi 10.4.2 and create a fresh VCL Forms Application. Per default the application should have the DPI support set to Per Monitor v2 in its manifest. If that is not the case, make sure it is.
The goal we want to achieve is to have a main form with two buttons and a second form dynamically created on each button click. One of the buttons creates the form just as usual, so the form will take the DPI awareness of the application. The other button creates another form instance from the same form class in DPI unaware mode (i.e. 96 DPI).
So we first add a second VCL form to the application and drop some controls on to it, so we can distinguish the form instances by its display quality on a high DPI monitor. I just assume that the second form class is named TForm2.
Now we get back to the first form and drop two buttons on the form and double click each to fill their event handlers. The first button gets the single line:
1 2 3 4 |
procedure TForm1.Button1Click(Sender: TObject); begin TForm2.Create(Self).Show; end; |
Make sure that the unit containing TForm2 is added to the uses clause.
The OnClick event of the second button needs a bit more code:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
procedure TForm1.Button2Click(Sender: TObject); var previousDpiContext: DPI_AWARENESS_CONTEXT; myForm: TForm; begin previousDpiContext := SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE); try myForm := TForm2.Create(Self); finally SetThreadDpiAwarenessContext(previousDpiContext); end; myForm.Show; end; |
So we temporarily change the DPI awareness of the current thread to unaware, create the form and restore the context to its previous state before finally showing the form.
Now we run this application and click the buttons. Each time a new form is shown with high DPI support or without depending on the button clicked. All forms live happily side by side in our still high DPI aware application.
Note, that we only switch context while creating the form. After that the DPI awareness is bound to that form instance and doesn’t affect others.
The advantage of this mixed DPI approach is, besides the sanity of the testers, that you can even release the application in high DPI mode to the public, of course with the limitation of some forms not being shown in high resolution. The latter should not really be a problem, because the alternative would be to have all forms shown that way. The wider audience will surface errors earlier and people are able to notice the progress.
That works well, I have personally been doing it with overridden constructor override, so a particular form becomes hdpi enabled independently from where it’s created.
But it can still be a very complex undertaking, and hard to test, f.i form created on 125% monitor moved to 200% will behave slightly differently than when created on a 200% monitor and moved to a 125% one…
The underlying issue being all the integer sizes throughout the VCL (in font size, dimensions, etc)
That sounds like it might be triggered by the upcoming release of Delphi 11 (aka 10.5). Guess all plugins there will face exactly this problem because the IDE becomes per monitor DPI aware and so Windows will treat all plugins as such. So I guess I’ll be adding a lot of SetThreadDpiAwarenessContext calls when it finally gets released.
(disclaimer: I’m not on the beta and I have no inside knowledge apart from somebody (not Uwe) telling me that GExperts looks really ugly in Delphi 11.)