If a TdxForm descendant is embedded into another form, it is scaled twice with DPI<>96.
I had to start descending from TdxForm for the embedded forms because otherwise an ExpressBars Toolbar would not be scaled.
Please run the attached sample project.
- When started on a monitor with 96dpi, pressing "Embed" results in this:
- If I move that window to a montor with 175% (e.g. by pressing Win+Right), it is scaled correctly like this:
- Now if I press embed again, so the existing embedded form is destroyed and recreated, it is scaled twice, resulting in this:
I tried to debug this and by the time I end up in TdxFormHelper.ScaleForPPI (starting from TdxForm.SetParent), the call to TdxFormHelper.ScalingBegin will actually cause the handle of the form to be allocated because of the IsWindowVisible(AContainer.Handle). By that time, the form is already scaled for the first time and then is scaled another time.
I found the tickets T809409 and T552235 which sound similar, but since both have been fixed and my problem still occurs with 20.1.4, my scenario seems to be different.
Sebastian,
Thank you for sharing your sample project! We will try to reproduce this behavior on one of our test machines with suitable environment. It may take us some time.
I will get back to you if we need some additional information.
Hi Paulo,
thanks for the feedback. But please be aware that you don't need a special environment to reproduce this bug. It is sufficient to run it on a machine that causes scaling - by comparing it to my screenshots or by debugging, it should be easily detectable that the embedded form is scaled twice.
I'm eagerly waiting for a fix for this as I have neither the option to not descend from dxForm or accept the double scaling.
You are right. We have reproduced this behavior on a regular machine.
It's been almost two weeks - any update on this? This is really a major issue for me, which is why I marked it urgent.
Hello Sebastian,
To my regret, we have not found a solution yet. The issue seems to occur only in Delphi 10.4 Sydney and is quite complicated. We may need some additional time.
When looking at TdxForm.CreateWnd it's not too surprising to see that this issue is a) only occurring with Delphi 10.4 Sydney and b) caused by the window handle being allocated:
{$IFDEF DELPHI104} if Parent <> nil then ScaleForPPI(TControlAccess(Parent).FCurrentPPI); {$ENDIF}
Indeed the issue goes away if I comment out these 2 lines, raising the question why this was introduced and whether there is an alternative solution?
Anyways, my proposed solution is this change in TdxForm.CreateWnd:
{$IFDEF DELPHI104} if (Parent <> nil) and (not TdxFormHelper.IsScaleChanging(Self)) then ScaleForPPI(TControlAccess(Parent).FCurrentPPI); {$ENDIF}
Since the issue is caused by the form getting scaled again, while a change of scale is already in progress, this fix seems appropriate to me. What's your opinion?
Note that there's identical code in TdxCustomForm.CreateWnd!
To be honest, this fix does not look valid. Although it looks logical at first glance, the form and its content cannot be correctly scaled without that trick. We have tried a similar approach and our automatic tests did not pass. Note that our controls are correctly scaled. Only the form is affected by this issue.
We may need some additional time to find a reliable solution.
I'm not trying to argue here, just posting my observations. My understanding is that the check for IsScaleChanging prevents a second nested scale at the beginning of the first scale (because of the window being created). I don't see how this is preventing the otherwise necessary call to ScaleForPPI under normal circumstances.
Also, using my attached sample project, step 3 shows the form exactly like step 2, so buttons and the form have indeed been scaled correctly. In my real-world project, the forms are much more complex, some with LayoutControl, some without - and they look fine after the fix.
I'm sure you guys have a much deeper understanding of this rather complex issue and most importantly a wider range of test-cases. So here's hoping you can come up with a universal fix in the not too distant future.
Thanks, Sebastian! I hope we will be able to find a universal solution soon.
In the meantime, I've discovered that even in Delphi 10.4, there's still a bunch of open bugs in the VCL regarding scaling (I'm sure that's old news to you).
But this has led me to find a workaround, which doesn't require a fix from you and since there's only one place where I'm creating those embedded forms, I'm fine.
RSP-31137 (evolved from RSP-19012) describes the workaround, which when applied to my demo looks like this:
procedure TMainForm.btnEmbedClick(Sender: TObject); var subForm: TEmbedForm; begin while Panel1.ControlCount > 0 do Panel1.Controls[0].Free; subForm:= TEmbedForm.Create(Self); subForm.HandleNeeded; subForm.ScaleForPPI(CurrentPPI); subForm.Parent:= Panel1; subForm.Show; end;
AFAIU this ensures a first scale before the Parent is actually set - which destroys the handle for unparented forms. The second scale caused by SetParent then does nothing (because CurrentPPI stays the same).