Very interesting. My custom TForm descendant (from which all of my forms derive) no longer gets the correct PixelsPerInch assigned during creation in Seattle Update 1. I don't override this property or change it, so something significant has changed in the VCL. I don't mind debugging but this is not what I was hoping to work on today. :-(

Comments

  1. That's correct. TForm.PixelsPerInch is now always what was set during DesignTime when saving the DFM. The current runtime value can be (and should have always been) taken from Screen.PixelsPerInch. The current behavior is more in sync with the documentation.

    ReplyDelete
  2. The bug is that PixelsPerInch is initialized to whatever is in the DFM UNLESS it's a TForm. A TForm gets updated to the Screen.Pixelsperinch property and is then scaled during creation (before loaded), but a class derived from TForm does not. The VCL contains SERIOUS problems with inheritance -- in this case, TCustomForm.Create checks to see if it is a TForm which completely breaks inheritance -- it should not care who has inherited from it!  It has DIFFERENT BEHAVIOR if the class being initialzed is TForm as opposed to something else inherited from TCustomForm or TForm. Near as I can tell, there is no way to change this. The introduces scaling bugs for derived forms that did not exist before Update 1.

    ReplyDelete
  3. This is the absolute most buggy update I have seen in recent memory, for Delphi. PAServer is crashing EVERY TIME the application closes and I have to sign out and back in on the target machine and restart Delphi on the dev machine EVERY time I run.

    ReplyDelete
  4. This just boggles the mind. Not only does the PixelsPerInch property now reflect the DesignTime value instead of the runtime value -- UNLESS IT'S A TFORM FOR CYRYING OUT LOUD, there is NO notification that the form is running on a new DPI unless you happen to move the form from one DPI monitor to a different one at runtime. I can understand that their tests probably do not take into account custom TForm descendants but then why TEST to see if the form being initialized is TForm and behave differently!? What a frustrating experience. There are ways to fix this but I am thinking YET ANOTHER DETOUR TO FIX A VCL BUG is in order. :-X

    ReplyDelete
  5. The check for TForm in TCustomForm.Create is just to make sure that form inheritance works by reading all the inherited DFM settings. It has been this way for quite some time now. The difference introduced by Update 1 lies in TCustomForm.ReadState, that doesn't update PixelsPerInch any more. Can you elaborate on what you are actually trying to achieve with the forms PixelsPerInch value?

    ReplyDelete
  6. Obviously PixelsPerInch should reflect the current PixelsPerInch of the form, since it can be moved between monitors.  AND IT DOES, if it is a TForm. If I derive from TForm to implement my own standard properties for my own use, the behavior changes -- even though I am not touching those properties. TForm.PixelsPerInch DOES report the correct monitor value as it should. It's just that when the dfm for a derived TForm subclass is read in, the value in the DFM overwrites the Screen.PixelsPerInch that was already set in the component, but it does NOT for a TForm.

    ReplyDelete
  7. Well, I take it back, the PixelsPerInch property is not updated to the screen's PPI, but the problem remains -- the objects on a TForm are scaled automatically when the form is created, but the objects on a form that descends from TForm are not.

    ReplyDelete
  8. Too add more confusion to the property, when the form is moved between monitors with different DPIs at runtime, the PixelsPerInch property magically now reports the PPI for the form on the monitor, rather than what was saved in the DFM.

    ReplyDelete
  9. Looking at procedure TCustomForm.WMDpiChanged(var Message: TWMDpi); it is quite clear that the documentation is still wrong.  The documentation should have been updated; the behavior of the scaling was working properly and should not have been halfway changed. :-(

    ReplyDelete
  10. I agree, that is at least inconsistent. Would you please file a QP report with a test case matching your findings?

    ReplyDelete
  11. I will try. My first order of business is to work around this. All of my forms no longer scale on high-dpi, and any functions that examine the pixelsperinch property have to be modified. I can't understand why any of this was changed to begin with -- the documentation could simply have been updated. :-(

    ReplyDelete
  12. Why does deriving from TForm change things? I can't help wondering if you've broken something.

    ReplyDelete
  13. David Heffernan I was really hoping I did! So I did NOTHING except derive an empty class and got this behavior. So I looked at the VCL source, and what do you know -- as a CustomForm is being created, there are checks all over the place to behave differently if the derived class is TForm or not.

    ReplyDelete
  14. And those checks use ClassType instead if an IS operator, so they are really hard-coded tests for TForm and only Vcl.Forms.TForm.

    ReplyDelete
  15. So it looks like I have to write yet another Delphi Detour to fix problems in the VCL. :-(

    ReplyDelete
  16. There hasn't been that much changed between Seattle and Seattle#1 in regards to source changes but there has been a change to TCustomForm.ReadState in regards to PixelsPerInch value being used.

    ReplyDelete
  17. Brandon Staggs What better way to spend a friday :)

    ReplyDelete
  18. Perfect example of breaking OOP principles. Class should never know or assume anything about its subclasses.

    ReplyDelete
  19. Nicholas Ring As I said, that is not  caused by checking against TForm. When do you actually create a TForm instance? All forms designed in the IDE are usually descendants of TForm, but not of ClassType = TForm.

    ReplyDelete
  20. Uwe Raabe I was just reporting what was changed from Seattle and Seattle Update 1 and there is no change that introduced a check to see if the current class is TForm or not.
    Since Brandon Staggs issue was that something changed from Seattle to Seattle Update #1, I thought my comment had some relevance but it seems it doesn't, so I apologise to all and will butt out now.

    ReplyDelete
  21. ClassType = TForm??? That's insane!!! If that's what is coded then I am appalled. It seems almost impossible that it could be so.

    ReplyDelete
  22. The problem with the changes to ReadState is that now a TForm Descendant's PixelsPerInch property, when read from the DFM, prevent the contents of the form from scaling. If the derived class does not have a dfm, there is no problem.  So if you create a base TForm class in code and try, the problem does not appear. You have to go through the trouble of creating a tform component with a dfm to see how royally messed up this seeming "small change" to the VCL makes things.

    ReplyDelete
  23. David Heffernan Search Vcl.Forms for ClassType.

    ReplyDelete
  24. I did overstate it with "all those checks." There's actually only one. :-)

    ReplyDelete
  25. Brandon Staggs Thank you! I used your solution from QC in my project and it solved the problems I had.

    ReplyDelete
  26. David Heffernan ClassType <> TForm actually does make sense in this scenario. The purpose is to read the values from the different DFM resources in the current visual inheritance chain. As TForm is the root of this chain and cannot have a DFM (only TForm descendants can have a DFM), it is perfectly valid to check for "equals TForm" instead of "is inherited from TForm". The example project given in RSP-12971 is just done plain wrong. If you change the parent class of a TForm descendant from TForm into something inherited from TForm in the PAS file, you also have to change the DFM from "object" to "inherited". The way it is done is not the way it is supposed to be done.

    ReplyDelete
  27. Uwe Raabe I am not an expert and working only as an hobbyist. If I undo the RSP-12971 modification (that worked for me perfect) and follow your explanation and change in the inherited form the DFM from object to inherited the problem is still the same. All forms have set in the IDE to the stsndard values PPI=96, ParentFont = false,  Scaled = true. With the RSP-12971 the FormPPI will be 120 at runtime (everything looks like in the IDE) and with your approach the FormPPI stays 96 and everything is downscaled.The font size is scaled down also. So, what is the right way and do you see a bug in Delphi or how can we handle it?

    ReplyDelete
  28. Karlheinz Jansen I haven't stated that changing the inheritance will solve the problem. I only said that form inheritance is not to be done the way it is done in the test case.

    ReplyDelete
  29. Uwe Raabe Yes, you are correct about the dfm error on my part. That is something I tested in my first sample project just to be sure it made no difference.  I forgot to change that in the DFMs of this sample project but it makes no difference to the reported issue.  I have learned that the expected OOP principles I usually design with need to be forgotten when it comes to "special" objects like TForm (and don't get me started on TFrame).

    ReplyDelete
  30. Uwe Raabe As it turns out, the issues with regard to inheritance, and TCustomForm checking to see what its real class type is, etc, had nothing to do with this problem. I perhaps wrote too many ranting comments as I worked through the issue to finally arrive at the cause and a solution. The DPI issue can be summed up this way: In Update 1, form scaling only works as expected for forms that directly inherit from TForm (and perhaps TCustomForm). Any other forms, which have ancestors before TForm, will have unpredictable scaling results. I use a common base form class, and my own modal dialog base class which inherits from that, all over the place. So the problem was immediately screaming in my face on my Surface Book from the first compile with Update 1. :-)

    ReplyDelete

Post a Comment