Delphi IDE treating TFrame as if it were a Form - delphi

Every once in a while when I am tweaking my TFrame classes (adding properties, methods, etc), the IDE gets confused and acts as if it thinks the frame is a form, complete with header/caption, borders, etc. Yet, clearly the class is declared as a TFrame descendent. Any ideas as to what causes this, how to prevent, and how to fix?
I'm using Delphi 2007 Pro. Also note (if it matters), the TFrame descendents are typically registered with the IDE (i.e. on the palette) via a design-time package.
Later: Additional "specifics": The frame that I'm having this problem with at the moment is, visually, a VERY basic TFrame (only change from brand new TFrame is size, and background color).
Here's its class declaration:
TBasePanel = class(TFrame)
private
FPanelManager: TPanelManager;
procedure SetPanelManager(const Value: TPanelManager);
protected
procedure Connect; virtual; abstract;
procedure Disconnect; virtual; abstract;
procedure Refresh; virtual;
procedure Requery; virtual; abstract;
published
property PanelManager: TPanelManager read FPanelManager write
SetPanelManager;
This frame is used as a base class for a number of others. I am usually editing it directly from the BPL project it belongs to (because all of these frames install to the palette), rather than as part of an EXE project, with related Forms open etc.
Also, "Embedded designer" is checked in Tools -> Options.
I am saving all DFM files as text rather than binary as well (if that matters at all).

I have encountered the same problem. The following steps solved the problem for us, it might also work for you:
in the IDE: close all forms that use the frame
open the frame, view as text (*.dfm)
the dfm probably begins with object MyFrame: TMyFrameClass
change this to inherited MyFrame: TMyFrameClass
I don't know what caused the problem.

Perhaps you had unchecked the 'Embedded designer' check box? (Tools | Options | Environment Options | VCL Designer). Then, indeed, your frame is shown at design time as a form (with caption, border etc.). Also a concrete code of your problematic TFrame descendant or more details about your case would help.

As far as I know, you have to have both the form and the frame open in the editor when you edit the frame. Else there can be update problems. Although I haven't seen this one.
But I gave up on frames a long time ago because I did not find them very reliable.
Right now I only use them for prototyping, creating a custom component (derived of a panel with the apropriate controls on it).

You may have to register custom module to the IDE
But your additional properties won't work well unless they are in ancestor class.

I have encounter a lot of problems with TFrame and finally came to such workaround that solves all my problems: I create and design frames visually, but use them only by hand-coding.
As a side effect my applications became smaller, because of less dfm-s.

Related

Delphi: 'Property ClientHeight does Not Exist'

My Delphi program builds and compiles fine, however as soon as it is run in the debug mode, I get the following error;
Property ClientHeight does Not Exist
After looking through all of the .DFM file sources, in every form the code is there which is;
ClientHeight = 111
I'm not understanding where I go wrong here?
Your forms would have been saved with a newer version of Delphi. Unfortunately you will need to open each form in the IDE and save it again to clear the newer properties. There is a tool that can help you called DFMCheck (http://andy.jgknet.de/blog/ide-tools/dfmcheck/). This is an add on that will go through all of your forms and tell you about any problems with the forms that will only show up at runtime.
The reason why you are seeing the problem is this. Delphi saves the forms with all of the properties. It uses streaming to load the forms at runtime. When it tries to load a form with properties that don't exist then you will get an error like this as the streaming system is trying to set a property on a component when the property doesn't exist.
I know this is old thread, but hopefully this will help others that has this problem.
In cases like this where your class inheriteds from other and you know the properties are there, just re-publish them .Add a published section and add them again for example:
published
property ClientWidth;
property ClientHeight;
This then forces the compiler to compile these typeinfo for parts where the parents classes might have forward declarations and thus , resolve your issue.
Hope it helps somebody, took me 3 days to get to the solution eventually.
Same bug happens in modern Delphi (e.g. Rio 10.3) with FMX frames. After some investigation it revealed to be caused by tweaking TFrame inheritance. Example below:
type
// Declaration of custom type
TFrameEx = class(TFrame) .. {here I override a couple of methods} end;
// Causes a bug (described below)
TMyFrame = class(TFrameEx)
// Works fine
TMyFrame = class(TFrame)
Explanation:
Due to changed type, Delphi was unable to correctly choose TMyFrame type between FMX and VCL. So when the TMyFrame was opened in IDE it would ask to strip out FMX properties (non-existent in VCL, e.g. Size.Width) and add VCL properties (e.g. ClientWidth). When saved, that would make the TMyFrame buggy - it would show the "Property ClientHeight does Not Exist" error in runtime upon init.
Had similar bug. First you need a dfm file for your frame.
When you inherit a frame, the dfm file must starts with "inherited MyFrame: TFRameEx" and NOT "object MyFrame: TFrameEx". Without the inherited, when I did it, it was adding TForm properties and in the editor the frame had TForm events, in Delphi 10.3. So delphi really needs the dfm to find the right type. If you use the ide menu, it will be done automatically. New->Others->inheritables it will create the dfm with the inherited line, create a file with {$R *.dfm} in it and a line in the project source "unitname in '......pas' {MyFrame TFrame};" Or you can do it by hand.
As for the possibility of having multiple frames in the same unit, havent tested it myself but since the line is {$R *.dfm} it might be doable.
wanted it to be a comment for the solution of kromster but cant comment apparently.
In my case, I was inheriting TFrame that was save in Delphi 7, and I change the .dfm to resolve.
The first line: "object" frmMain: TfrmMain
I changed to "inherited", like this: inherited frmMain: TfrmMain

Published interface properties bug and workarounds

I wrote a set of components that link to each other via published interface properties. They are registered and installed in a design package.
Using published interface properties is not that common in Delphi, and thus, unsurprisingly, doesn't seem to work that well.
It works fine when components reside on the same form, however interface property links between components on different forms cause issues.
Unlike object links to components on another form, interface links don't seem to be recognized by IDE. What I mean is best described by an example, when you have 2 forms open in IDE, and have links between components on them, then trying to switch to form view as text (Alt+F12) would cause IDE to correctly complain that:
Module 'UnitXXX.pas' has open descendents or linked modules. Cannot close.
But if the property is an interface then this does not happen, what happens instead is that the link is severed (and that's the best case scenario when you use Notification mechanism to clear references, otherwise you're left with an invalid pointer)
Another problem, likely as a consequence of the same bug is that when you open a project in IDE, the order in which forms will be reopened is undefined, so IDE can try to open a form that contains components that have interface links to components on another form, but that other form is not recreated yet. So this effectively results in either AV or severed links.
Back in 90s while I used Datasets and Datasources I remember similar issues with links between forms disappearing, so this is somewhat similar.
As a temp workaround I added duplicate published properties, for each Interface property I added another that is declared as TComponent. This makes Delphi aware there is a link between forms, but is an ugly workaround to say the least.
So I wonder if there is something I can do to fix this issue ? It's an IDE bug and likely not fixable directly, but perhaps I can override something or otherwise hook in to streaming mechanism to more effectively workaround this bug.
I haven't ever gone so deep into streaming mechanism, but I suspect the Fixup mechanism is supposed to deal with this. There is a csFixups TComponentState so I hope a workaround is possible.
Edit: Using D2007.
Update:
New updated reproducible example uploaded to http://www.filedropper.com/fixupbugproject2
Added property ComponentReference: TComponent so that it's easy to compare and trace interface vs component streaming.
I narrowed the problem down to assembler level which is a bit out of my depth.
In procedure GlobalFixupReferences in classes unit it calls:
(GetOrdProp(FInstance, FPropInfo) <> 0)
which eventually executes:
function TInterfacedComponent.GetInterfaceReference: IInterface;
begin
// uncomment the code bellow to avoid exception
{ if (csLoading in ComponentState) and (FInterfaceReference = nil) then
// leave result unassigned to avoid exception
else
}
result := FInterfaceReference; // <----- Exception happens here
end;
As you can see from the comment, the only way I found to avoid the exception is to leave the result unassigned, but that breaks the functionality since comparison above in GlobalFixupReferences fails due to GetOrdProp <> 0, which severes the link.
tracing deeper the more exact location of exception is in
procedure _IntfCopy(var Dest: IInterface; const Source: IInterface); in system unit
This line in particular raises an read of address 0x80000000
{ Now we're into the less common cases. }
##NilSource:
MOV ECX, [EAX] // get current value
So, why MOV fails and what's wrong with ECX or EAX I have no idea.
To summarize, the problem happens only with published interface properties that have a getter method, and the property points to component on another form/module (and that form/module is not recreated yet). In such case restoring form DFM causes an AV.
I'm pretty sure the bug is in the ASM code in GetOrdProp, but it's beyond my ability to fix, so the
easiest workaround is to use a Field instead of a getter method and read it directly in the property. This is, fortunately good enough for my case currently.
Alternatively, you can declare the property as TComponent instead of interface, then write a TComponentProperty descendant, override ComponentMayBeSetTo to filter component that don't support the required interface. And of course register it using RegisterPropertyEditor

Is there a component to automatically create a config form?

I have a settings class in my system, where I store configuration values for important settings. It looks like the code below, but with many more attributes of different types.
Now I need to implement a form with which I can adjust and configure these settings at runtime. Its very cumbersome to implement the fields for each attibute and guarantee that all values are mapped without error.
Now my question: is a vcl component that could automatically create an interface to solve that. Eg. offer a tree-like or listview-like interface with the names of the attributes and fields to edit the values (like the property pane in the IDE, with printer settings, see screenshot below). That would be a great thing. No?
How do you deal with configuration forms like that?
Thanks for your input!
TGoldmannSettings = class
private
FInitialSymbolSize : Integer;
FPenWidth : Single;
FCanvasColor : TColor;
FShowLiveCoordinates : Boolean;
FFont1 : TFont;
FMsmPointSymbol : TAvailableSymbols; // own type
...
public
constructor Create;
destructor Destroy; override;
property SymbolSize : Integer read FInitialSymbolSize write FInitialSymbolSize;
property Font1: TFont read FFont1 write FFont1;
...
published
property PenWidth: Single read FPenWidth write FPenWidth;
property CanvasColor: TColor read FCanvasColor write FCanvasColor;
property ShowLiveCoordinates: Boolean read FShowLiveCoordinates write FShowLiveCoordinates;
...
end;
You sometimes find something I mean in printer setting dialogs:
The TJVInspector component from the Delphi Jedi JVCL project creates a property editor very similar to what you are looking for. They have an advanced example that works on an INI file.
JVCL Site: http://jvcl.delphi-jedi.org/
Nice example: http://www.neugls.info/?tag=tjvinspector
The JVCL / JCL package is huge but has a ton of useful components and functionality.
I have never yet created an automatic-configuration form generator similar to the one in the delphi project options, but I have seen this done in several projects that I work on, and seen the source code, and it works very much like this:
I would have a base type of frame called TConfigFrameBase and it would contain some properties like this: Caption (user displayed name of property), Hint (some help), and Name (config property), and Section (page this property is shown on).
Specialized inherited frames would be used for boolean properties, string properties, etc. Your domain (your app) will have its own custom types. Dates? Lists of apothecary locations in Denmark? Only you know for sure the complete set of UI configuration property types you need, and that's why I haven't seen a component that makes this automatic or just a component. The boolean frame would contain a Label control and a checkbox and would have a default height around 30 pixels. A frame that let me move a list of options on side A to options on side B (columns visible within a particular grid, for instance) might be as high as 300 pixels. By vertically stacking these frames, in a scrollbox, you don't have to do much thought about layout. Everything will be usable when these frames are used to populate a listbox.
A tree view on the left that lets you pick a section. In the on-click in the tree view, the right side pane is built by iterating through my internal list of config-frames that are registered in a list or dictionary, and filtered by the Section they belong to.
I wouldn't use a JVCL Property Inspector as my configuration control, but it might work for you. I also don't think you're going to get everything you need out of VirtualTreeView, but your mileage might vary. You can write your own custom Editor controls, and if you like writing in-place-editor controls, you might find VirtualTreeView perfect.

Delphi ignores CS_OWNDC flag when creating TFrame component, wglMakeCurrent subsequently fails

I'm trying to put together some components for using OpenGL in RAD Studio 2009. I want to have multiple Rendering Contexts.
My idea is to have something like a "main OpenGL component", called GLMaster. It's a TFrame descendant so it should provide me with a DC. Also there's a component for a GLMonitor, acting as camera.
I create an OpenGL RC in the overriden GLMaster.Loaded inside an "if not (csDesigning in ComponentState) then"-clause. The DC for this I do not keep, it's stated in the RAD Studio help that you shouldn't:
"TWinControl.Handle
Do not refer to the Handle property during component creation or streaming. The underlying window does not exist until the first time the Handle property is referenced. When this occurs, the HandleNeeded method is called automatically."
I handle this by function pointers in components using GLMaster pointing to GLMaster.GetCurrentDC (returns a HDC).
During destruction GLMonitor wants to clean up some render-to textures and other OpenGL resources. When retrieving the DC for a call to wglMakeActive the function pointer is followed and execution jumps to GLMaster.GetCurrentDC. Pointer(Self) tells me that it's the same GLMaster that we created the "master RC" in during the streaming of the components. The property [GLMaster.]Handle is now invalid! If I however free the GLMonitor in the app form's OnClose (using simply GLMonitor_1.Free;) the Handle inside GLMaster.GetCurrentDC is valid end everything works.
I managed to see that the handle used in GLMaster.Loaded is not the same (!?!?) as after the initialization is done and the app form is showed to the user. When I found this I googled my problem and added an overriden CreateParams to add CS_OWNDC to the GLMaster component. A breakpoint there tells me that it's being executed.
Why does the VCL/Delphi silently provides the newly created components with other Handles, and thereby indirectly other DCs, even if I provide the OWNDC flag? Is there someway to get an "ALL components are now loaded and their properties are read from the .dfm file and"-message so I can run GLMaster.InitEverything inside the component?
Right now I'm thinking the fastest way onward is to put this InitEverything in the main form's OnShow handler. And then a call to GLMatser.CleanAll in the main form's OnClose. But I don't want any code in the form, that's the reason I created the components in the first place!
P.S.
Not that I think it matters for this particular problem but I use OpenGL 3.2 Core Profile.
I'm answering the "Why does the VCL/Delphi silently provides the newly created components with other Handles".
Why an control's Window Handle might change at run-time
Your TFrame (an TWinControl descendant) is placed on an other TWinControl, let's say a TForm. The parent container provides property wrappers around many settings, allowing us to easily make changes; Some of those changes can't be implemented without re-creating the windhow handle. You can get a long list of properties that cause this to happen by doing a search for RecreateWnd in Forms.pas, Controls.pas etc.
Examples of properties that call RecreateWnd to implement a change at run-time:
TScollBox.BorderStyle
TForm.BorderIcons (for MDI childs)
When your TFrame's parent needs to change Window Handle, your TFrame is going to have to change Window Handle as well. Your Delphi Control, no matter the descendense, needs to be handle this, and other controls have it worst: Controls that are implemented as wrappers around Windows controls need to save state and reload state (TEdit, TComboBox, ...)
What I think is going on
While streaming (loading your form) some control does something that needs a Window Handle before loading finishes. It's very likely your own code! After everything finishes loading the Form might need to re-create it's Handle, and this in turn causes your Frame's handle to be changed.
What you might want to override
Given the way the VCL works, you need to be prepared for your Window Handle to change. You should do a search in Controls.pas for the words Handle, HDC. If your control is so intimately linked to it's Window Handle and HDC, it's in your best interest to read up on this stuff.
Take a look at those routintes. Maybe you can sopot a better place to hook:
CreateHandle
CreateWnd
DestroyHandle
DestroyWnd
GetDeviceContext
If the title is the question, then no, Delphi does not ignore CS_OWNDC when creating a frame. Just tested with the below procedure with a frame on a form. When the flag is not specified, the line is drawn on itself again and again, resulting with a line from (0,0) to (10,10). When the DC is owned, the line extends from (0,0) to (50,50), no need to tell but the retrieved DC is same at all times.
procedure TFrame2.WmPaint(var Msg: TWMPaint);
var
i: Integer;
DC: HDC;
Pt: TPoint;
begin
inherited;
for i := 1 to 5 do begin
DC := GetDC(Handle);
GetCurrentPositionEx(DC, #Pt);
LineTo(DC, Pt.X + 10, Pt.Y + 10);
ReleaseDC(Handle, DC);
end;
end;
if your question is this:
ALL components are now loaded and their properties are read from the .dfm file and
The answer is:
Yes, just override the Loaded method of your component. It is called when all the references are valid (after dfm reading is finished).

TFrame inheritance refactoring

Yet another TFrame IDE-registered-component question from me. Thanks for all the help, fellow programmers. : )
Playing around with Darrian's TFrame inheritance suggestion here:
Specifics:
Basically, I have a TFrame-based component that I've registered to the IDE, and it has worked wonderfully. I'm now developing a few "sister" components which will share a great deal of the existing component's non-visual functionality and properties. It makes sense, then, to move a lot of that to a parent/superclass which both the new and the old components can then inherit from.
What is the best way to "refactor" TFrame inheritance in this way? (This may apply to TForm-class descendants too, not sure). What are the caveats and things to watch out for?
Example:
I tried, for example, creating a new TFrame, with nothing on it, and calling that frame TMyBaseFrame. Then modified the class definition of my existing component (Let's call it TMyFrameTreeView) to inherit from that rather than TFrame.
It compiled fine, but when I tried dropping it on a form, I got "ClientHeight not found" (or "ClientHeight property not found"), and it wouldn't drop on the form. Deleting ClientHeight and ClientWidth from the related DFM wreaked havoc, and they ended up replaced upon resizing anyway. I noticed ExplicitHeight and ExplicitWidth in descendent classes, and am thinking that relates to property-value overrides from inherited values, but am not sure. Recreating an entirely new frame via New -> Inherited Items, and then copying everything over, hasn't yielded great results yet either.
Final Note
I realize this could get messy quickly, with streaming DFM files and multiple generations of descendants, etc.... which is part of why I'm asking for the overall "things to look out for" conceptual aspect, but also giving a specific real-world simpler version of the problem as well (which seems to me, ought to be doable).
I've created a little test package to hack around in learning attempts, and am learning a great deal, but it's slow-going, and any guidance/insight from you Delphi "Jedi Masters" out there would be MOST appreciated. : )
Answer update later:
Both of the answers below were helpful. As well, creating a "Base Frame Class" which has NO changes from the normal TFrame, and THEN inheriting from that before adding any properties, methods, etc. seems to stabilize the inheritance streaming tremendously. Not sure why, but so far it has.
In addition to changing base class of TMyFrameTreeView to TMyBaseFrame change the first word in the dfm file for TMyFrameTreeView from object to inherited.
I'm now developing a few "sister"
components which will share a great
deal of the existing component's
non-visual functionality and
properties. It makes sense, then, to
move a lot of that to a
parent/superclass which both the new
and the old components can then
inherit from.
What is the best way to "refactor"
TFrame inheritance in this way?
The crux of your text above perhaps is "component's non-visual functionality". So, in this case, IMHO it's best to separate the visual and non-visual layers.
So, perhaps it's better to use a decorator:
TMySharedEngine = class(Whatever)
property LinkedFrame: TFrame;
property P1;
property P2;
...
procedure Proc1;
procedure Proc2;
... //etc.
end;
and in your 'sister' frames to use instances of it:
var
TMyFrame1 = class(TFrame)
...
FDecorator: TMySharedEngine;
...
public
property MySharedPart: TMySharedEngine read FDecorator;
constructor Create(AOwner: TComponent); override;
...
end;
constructor TMyFrame1.(AOwner: TComponent); override;
begin
inherited;
FDecorator:=TMySharedEngine.Create; //ok, ok do not forget to Free it .Destroy
FDecorator.LinkedFrame:=Self;
...
end;
OTOH, if you want to use your approach you can use Visual Form Inheritance (as Darian suggested) or (more flexible) you can do it by hand: Create, using the IDE the following frames: TBaseFrame, TChildFrame1, TChildFrame2 ... etc. Now go on TChildFrame1's unit and change by hand it's class definition from TChildFrame1 = class(TFrame) to TChildFrame1 = class(TBaseFrame). Compile. It should work. It's recommended though that when you'll do this trick TBaseFrame to be empty in order to avoid possible small quirks (feature collisions etc.)
HTH.

Resources