Jump to content
dan13l

Migration issue - StyleElements and DoubleBuffered in DFM when inheriting forms even though the values are default

Recommended Posts

Posted (edited)

Hello, can someone elucidate me on the following phenomenon in Delphi which presently I consider to be a bug (I have reported it but am hoping someone here could answer me sooner):

 

I am working on a project with many (100s) forms that inherit from a base form. Ever since migrating to Delphi 12 (from Delphi 7) I have been encountering these annoying changes in the IDE whereby inheriting forms have certain properties that are written out but I believe they shouldn't be. For example, take this base form DFM file:

object BaseForm: TBaseForm
  Left = 0
  Top = 0
  Caption = 'My Form'
  ClientHeight = 441
  ClientWidth = 624
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -12
  Font.Name = 'Segoe UI'
  Font.Style = []
  TextHeight = 15
  object BitBtn1: TBitBtn
    Left = 280
    Top = 232
    Width = 75
    Height = 25
    Caption = 'BitBtn1'
    TabOrder = 0
  end
end

It is the default that the IDE generates when creating a new form, except that I've changed the Name, Caption and dropped a TBitBtn on it.

 

When creating a new form and inheriting from the above form, the DFM looks like this:

inherited MainForm: TMainForm
  StyleElements = [seFont, seClient, seBorder]
  TextHeight = 15
  inherited BitBtn1: TBitBtn
    DoubleBuffered = True
  end
end

I don't believe any of the properties should have been written out into the inheritor's DFM. TextHeight doesn't bother me as much because it already exists in the Delphi 7 code base, and frankly I'd rather leave it. It's the new stuff added by Delphi 11 & 12 (according to posters on here) to do with the DoubleBuffered property, and earlier versions that introduced the StyleElements property. All the forms I work on and save in Delphi 12, I get this extra stuff generated that I don't agree should be generated, and I am currently holding off committing these spurious changes until I have it on good authority that those are valid changes. I have a custom tool that I use to open all project's forms in the IDE and re-save them. I have it sorting out bits like ClientWidth and ClientHeight (converting from Width/Height), and I check each change and allow for things like the removal of OldCreateOrder (it then modifies the constructor code for affected forms if they have any code after the "inherited" part - caused a lot of precedence issues otherwise) and PixelsPerInch properties, or changes in the order in which properties and their values are written out in the DFM, and other changes in the DFM as part of the migration that I am happy to commit (component versions for instance). It may seem like over the top (I often ask myself that), but it doesn't seem so now as this practice showed that it (writing a fair bit of automation code) was all worth doing.

 

Worth noting, I run the IDE in TSRemote Desktop/RDP/RDS environment, and the application itself is primarily run via Citrix. I run Delphi 12.1 with the patch.

 

I've done some digging into the DoubleBuffered property and the recent changes to it, and I've found that the TBitBtn's DoubleBuffered property at design time always shows as False (its default is True, and the internal property variable is set to True) - turns out the IDE detects RDP sessions and this affects the designer and what it writes into DFM for the DoubleBuffered property. I guess the IDE's TApplication has similar code for detecting TS sessions, but it doesn't seem to be the same as in Delphi 12. The reason I think this is because I can't set TBitBtn's DoubleBuffered property to True, even though the DoubleBufferedMode is set to dbmRequested. I am guessing the Delphi 12 IDE runs some former code that attempts to improve RDP performance which doesn't yet have the DoubleBufferedMode property (Delphi 11's code?). I wonder if it's possible to stop the IDE detecting RDP sessions?

 

(EDIT2) I think because Emba combined DoubleBuffered's getter with the function CanUseDoubleBuffered, so to achieve application-wide DoubleBuffered improvements without components needing to change, it looks like a design smell because I believe the DoubleBuffered getter should be separate from the function CanUseDoubleBuffered. Moreover, because the DoubleBufferedMode is set to dbmRequested by default (not dbmDefault 😄), making the RDP optimization not on by default (i.e. DoubleBuffered is as it was before), you'd have to go through your controls and set DoubleBufferedMode to dbmDefault to enable this feature. I've read here that it was in D11 they've made changes to this and then again in D12, so maybe this explains the design change, maybe they've turned on RDP optimization by default, then backed it out due to possible issues, or maybe they've just changed DoubleBufferedMode to dbmRequested, not dbmDefault which was probably the original choice? For convenience, here's the DoubleBuffered getter and the function it calls below it:

function TWinControl.GetDoubleBuffered: Boolean;
begin
  Result := FDoubleBuffered and (CanUseDoubleBuffering or (csWriting in ComponentState));
end;

function TWinControl.CanUseDoubleBuffering: Boolean;
begin
  Result := (FDoubleBufferedMode = dbmRequested) or not (Application.InRemoteSession and Application.SingleBufferingInRemoteSessions);
end;

What I still don't understand, why I can't set DoubleBuffered to True on a TBitBtn and get the Object Inspector to show it as True? DoubleBufferedMode is set to dbmRequested so based on above code, I should see "True" in the Object Inspector. Could it be the BPL that contains the TBitBtn is running some old code, and not the one pasted above??? This is so weird. Have I lost my mind? 🙂 No, probably Delphi is buggy AF as usual.

(END OF EDIT2)


While writing this, I've found that if you set DoubleBuffered to False, even if the Object Inspector already shows it to be False, the DFM will now have that property written out with False. Then, the inheriting form doesn't have this property written out anymore (going to have to go with this unsavoury workaround for now). But if you want to leave the DoubleBuffered as the default/True, then the inheriting form will have it written out.  It's as if the IDE doesn't detect that the value of a property in the inheriting form hasn't really changed from base, it's only the function in the base class that returns False due to some runtime condition (which again, in this case, is affected by IDE's detection of RDP environment). I don't understand then, why the IDE doesn't write the value into base if the getter returns False, but writes it into the inheriting form DFM if the getter for the base class property is false. Seems like a bug in the IDE to me.

 

The bit with StyleElements is odd as well in that if I change the base form's StyleElements to [], it now appears in the base DFM (that's fine because it's no longer the default value), and when deleted from the inheriting form (or newly saved by Delphi 12 when coming from Delphi 7), it doesn't get re-added by the IDE (going to go with this, as probably won't use styles; although on one hand this seems like workaround, it's "less bad" than the one above with DoubleBuffered; on the plus side, the IDE no longer shows the forms as styled!).

 

Logged the issue on the new Quality Portal here. Thanks

 

 

P.S. I've mentioned styles here - can someone weigh in with a sentence or two and recommend or not recommend them? My superiors could be very pleased to see a different look and feel in the application after so many years of the same monotonous grey design (I've no issue with it though). But I feel that I shouldn't use styles, I also feel it may be buggy; also seeing as customers mostly use Citrix, it may be better performance to leave it all as is. One more thing, there is the option of using Developer Express's skins instead - are those any better? Sorry, I feel like I should have created a separate thread for this.

 

EDIT: tweaking, punctuation, clarifications.

Edited by dan13l
Added a paragraph called EDIT2

Share this post


Link to post

Behavior of saving value of property in DFM is controlled by Storage Specifiers:
1. It's Storage Specifier "default" value. So, if your property defined like:
 property MyProperty: boolean read FMyProperty write FMyProperty default true;
and on moment when you save form into DFM value of your property MyProperty is true, it's will be not written into DFM file.
2. It's Storage Specifier "stored". by default it's TRUE, but you can write constant FALSE or use Boolean function to change behavior for specified situations.

More details here

So, for your case, you can try to redeclare published properties in class TBaseForm with correct (for your opinion) Storage Specifiers.


About changes in DoubleBuffering and RemoteDesktop in new versions of Delphi you can read here

 

About StyleElements: it's used only on case when you use VCL styles, so if you don't plan to use it - you can just ignore this properties.

 

P.S. If you want to know which parts of sources IDE use in debug mode, you can debug one Delphi instance from another. Just write some BPL, intall it into IDE, got o Run->Parameters->Debugger->Host Application and set it into "C:\Program Files (x86)\Embarcadero\Studio\23.0\bin\bds.exe", then you can run Delphi with debugging, put breakpoints and see which methods does it use and why save some params. 
I think you need to check some TWinControl.IsDoubleBufferedStored method, TWriter and TReader classes, etc.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×