dan13l 0 Posted Monday at 05:25 AM I am having a weird issue with Delphi which at first looked like a bug in the IDE, but now I'm thinking it's a combination with me not doing something right. The problem is as follows: - I have a bunch of TDataModules in the project and I needed to change them to descend from a base class that adds a method and some data the descendants can use. - The base class won't contain any designable items (no published elements), so I don't necessarily need a base DFM. - So I went and changed all the places that descend from TDataModule to descend from the new base class; all builds and runs fine. However, when I open the descendant data module in the designer, it opens it as a form and so adds a bunch of form related properties to the DFM, after which I will get runtime errors such "Error reading MainDataModule.ClientHeight: Property ClientHeight does not exist." For completeness, here is the PAS for the base data module: unit uBaseDataModule; interface uses System.Classes; type TBaseDataModule = class(TDataModule) private FSomeList: TList; protected procedure SomeMethod; end; implementation [...] end. And here is the PAS for the descendant data module: unit fMainDataModule; interface uses System.SysUtils, System.Classes, uBaseDataModule; type TMainDataModule = class(TBaseDataModule) private { Private declarations } public { Public declarations } end; var MainDataModule: TMainDataModule; implementation {%CLASSGROUP 'Vcl.Controls.TControl'} {$R *.dfm} end. The DFM for the descendant looks like this: object MainDataModule: TMainDataModule Left = 0 Top = 0 Height = 480 Width = 640 end If I did it "properly", the DFM above would inherit the base DFM, so the keyword object would have to be changed to inherited, as I'm sure you know. So I am aware I'm doing things in a slightly hacky way, I just figured if it works for forms, it should work for data modules, but for some reason Delphi assumes the data module is a form. This is what the descendant's DFM looks like after opening the designer and saving (notice the form related properties having been added): object MainDataModule: TMainDataModule Left = 0 Top = 0 ClientHeight = 402 ClientWidth = 608 Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -12 Font.Name = 'Segoe UI' Font.Style = [] TextHeight = 15 end On the project I'm working on, there was already a base form - without a DFM - with many forms descending from it, with the designer not having any issues. I'm really curious to know how Delphi determines what is a form and what is a data module when it opens the DFM in the designer. My short term solution is to create a DFM file and change all descendant data modules to say inherited instead of object, and add the base data module to the DPR with the correct bits going into the bit inside the curly braces like so (I simply re-added the base data module unit after adding the {$R *.dfm} bit): uBaseDataModule in 'uBaseDataModule.pas' {BaseDataModule: TDataModule}; As an aside, it's weird that Delphi assumes there is a global variable named BaseDataModule. There isn't one defined. It appears to be using this name ("BaseDataModule") when going to File -> Add -> Other -> Inheritable Items. Otherwise, in there, it's just "TDataModule". Surely it should use "TBaseDataModule", not the name defined in curly braces. Similarly, I've noticed that the DFM's root object also has a name, i.e., "MainDataModule: TMainDataModule". What's the point of that? Surely it should just be the type as there may not be a MainDataModule global variable (removing it doesn't break anything; in fact, putting anything for the name works, but Delphi will correct it after saving). This is probably some really ancient Delphi stuff that isn't going to change any time soon, but I wonder if anyone has a workaround so I can avoid needlessly having a base DFM for all my data modules. All I wanted was to add a method and some data to existing data modules, so having them descend from a base data module looked like the way to go. Share this post Link to post
PeterBelow 250 Posted Monday at 11:06 AM To make the inheritance work for designer classes like a data module your TBaseDataModule has to have a DFM-file, even if it is practically empty, but it must contain a valid Name property. The simplest way to get one is to create a new data module in the IDE and change its Name property to BaseDataModule, then save the unit under the name you want. You can now change the inheritance of your TMainDataModule in the editor, but you also have to switch the designer for that module to the dfm view and there change the first "object" keyword to "inherited". And make sure your TBaseDatamodule is not in the list of autocreated items! You can delete the BaseDataModule variable the IDE created for you, it is not needed. Share this post Link to post
dan13l 0 Posted Monday at 11:28 AM (edited) @PeterBelow, thank you but my question was how to have a base data module class without a DFM. This is possible with forms, but when it comes to doing the same thing with data modules, Delphi's designer treats data module as a form when it clearly shouldn't. EDIT: I may not have been clear, so here's a small example. You can do this with forms: Say you have a form: TForm1 = class(TForm). You then want to change it to inherit from a base class, e.g. TBaseForm (say, you want to add something to all descendants, but you don't want the base class to be designable). You can simply declare TBaseForm = class(TForm) - no DFM here, and simply change the code to inherit from TBaseForm instead of TForm. This works fine. If you were to then open the designer for TForm1, it would work as normal. Doing the same thing with data modules works as in the code would compile and run without problems, but if I open the designer for such a data module (descendant data module), it shows a form and screws up the DFM (after which the code won't run :D) Edited Monday at 11:39 AM by dan13l Clarification with example Share this post Link to post
PeterBelow 250 Posted Monday at 04:38 PM (edited) OK, I would not do it this way, but your problem is probably a missing FormType node in the DPROJ file. Look for nodes "DCCReference Include" and see how a "uninherited" datamodule is defined there. Compare with one of your derived datamodules. Edited Monday at 04:38 PM by PeterBelow Share this post Link to post
Hans J. Ellingsgaard 22 Posted Monday at 09:24 PM 9 hours ago, dan13l said: PeterBelow, thank you but my question was how to have a base data module class without a DFM I don't think you can. I've been following the same method as PeterBelow describes, if not, I'm having the same problems as you. Share this post Link to post
Anders Melander 1951 Posted Monday at 11:02 PM Use an interposer for TDataModule. The datamodule base class (no DFM): unit Foo.Bar; interface uses Classes; type TBaseDataModule = class(TDataModule) private FFoobar: integer; public property Foobar: integer read FFoobar write FFoobar; end; implementation end. Your datamodule: unit Whatever; uses Classes, etc. etc., ... Foo.Bar; type // The interposer // You could have declared it in the Foo.Bar unit but I prefer to declare it explicitly // everywhere it's used to make it clear what is going on. TDataModule = TBaseDataModule; // Your regular TDataModule stuff type TMainDataModule = class(TDataModule) ... end; etc. Share this post Link to post
dan13l 0 Posted 20 hours ago @Anders Melander Thank you, that's what I was looking for. The designer is happy now 🙂 I used this before, just didn't realise it would work here/my mind didn't connect the dots. Thank you again. @Hans J. Ellingsgaard What's with the negative attitude 🙂 In Delphi, what can't you do 🙂 @PeterBelow I've tried your suggestion looking in the dproj, just in case it would also work but it didn't. Inheritable and non-inheritable forms/data modules look identical there. It was worth a shot. Maybe the following logic is flawed somewhat but I think if dproj is missing and is recreated from dpr, it would be pretty annoying having to manually edit things in there to ensure things work as expected (but I realise maybe I'm doing something a little weird). Thank you all for input. Share this post Link to post