Delphi object inspector doesn't show TFrame descendants's additional properties by design. People tend to suggest using a known trick which is commonly used for showing TForm descendant's properties on the Object inspector. The trick is: registering custom module for TForm descendants to Delphi IDE via design time package like:
RegisterCustomModule(TMyFrame, TCustomModule);
The object inspector can show additional properties of the TFrame Descendant's instance with this way but it loses its frame behaviours while it is embedded in a form. Not redesignable, not possible to implement events for its subcomponents and it accepts child controls (which it musn't). But it behaves normally in its own design area.
Looks like, those behaviours provided by Delphi IDE specially just for TFrame. They problaly are not kind of generic facilities.
Is there any other way to accomplish this without losing frame behaviours ?
I'm using Delphi 2007
@Tondrej,
Read comments for the problem, thanks in advance.
frameunit.dfm :
object MyFrame: TMyFrame
Left = 0
Top = 0
Width = 303
Height = 172
TabOrder = 0
object Edit1: TEdit
Left = 66
Top = 60
Width = 151
Height = 21
TabOrder = 0
Text = 'Edit1'
end
end
unit frameunit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TBaseFrame = Class(TFrame)
protected
Fstr: string;
procedure Setstr(const Value: string);virtual;
published
Property str:string read Fstr write Setstr;
End;
TMyFrame = class(TBaseFrame)
Edit1: TEdit;
private
// This won't be called in designtime. But i need this to be called in designtime
Procedure Setstr(const Value: string);override;
end;
implementation
{$R *.dfm}
{ TBaseFrame }
procedure TBaseFrame.Setstr(const Value: string);
begin
Fstr := Value;
end;
{ TMyFrame }
procedure TMyFrame.Setstr(const Value: string);
begin
inherited;
Edit1.Text := Fstr;
// Sadly this code won't work and Edit1 won't be updated in designtime.
end;
end.
unit RegisterUnit;
interface
procedure Register;
implementation
uses
Windows, DesignIntf, frameunit;
procedure Register;
var
delphivclide: THandle;
TFrameModule: TCustomModuleClass;
begin
delphivclide := GetModuleHandle('delphivclide100.bpl');
if delphivclide <> 0 then
begin
TFrameModule := GetProcAddress(delphivclide, '@Vclformcontainer@TFrameModule@');
if Assigned(TFrameModule) then
begin
RegisterCustomModule(frameunit.TBaseFrame, TFrameModule);
// Just registering that won't cause Tmyframe to loose its frame behaviours
// but additional properties won't work well.
//RegisterCustomModule(frameunit.TMyFrame, TFrameModule);
// That would cause Tmyframe to lose its frame behaviours
// But additional properties would work well.
end;
end;
end;
end.
Which custom module class are you registering for your frame? Which version of Delphi are you using?
From my experiments with Delphi 2007, the custom module class which seems to work is TFrameModule. This class is contained in delphivclide100.bpl. Since there is no corresponding delphivclide.dcp you have to load it manually:
unit FrameTestReg;
interface
procedure Register;
implementation
uses
Windows, DesignIntf,
FrameTest;
procedure Register;
var
delphivclide: THandle;
TFrameModule: TCustomModuleClass;
begin
delphivclide := GetModuleHandle('delphivclide100.bpl');
if delphivclide <> 0 then
begin
TFrameModule := GetProcAddress(delphivclide, '@Vclformcontainer@TFrameModule@');
if Assigned(TFrameModule) then
RegisterCustomModule(TTestFrame, TFrameModule);
end;
end;
end.
My FrameTest unit is very simple, it has no FrameTest.dfm, only the declaration of the new TFrame descendant:
unit FrameTest;
interface
uses
Forms;
type
TTestFrame = class(TFrame)
private
FHello: string;
published
property Hello: string read FHello write FHello;
end;
implementation
end.
Using TFrameModule class, everything seems to work fine so far. I can create a new descendant of TTestFrame to include in the project and edit its published properties in the Object Inspector, put instances of this new descendant on a form in the IDE, edit their new published properties in the Object Inspector, write event handlers for their child components etc. In the .dfm resource I can see the expected "inline" directive for the instances. I haven't encountered any problem with it so far so perhaps this is the solution.
There is no need to do in "hack way"
uses
...
DMForm,
VCLFormContainer,
...
procedure Register;
begin
...
RegisterCustomModule(TYourFrameClass, TFrameModule); // for frames
RegisterCustomModule(TYourModuleClass, TDataModuleCustomModule); // for data modules
...
end;
There is another way around to add frames too
type
TNestableWinControlCustomModule = class (TWinControlCustomModule)
public
function Nestable: Boolean; override;
end;
function TNestableWinControlCustomModule.Nestable: Boolean;
begin
Result := True;
end;
+
RegisterCustomModule(TYourFrameClass, TNestableWinControlCustomModule);
Names of units (tested in XE7):
TCustomModule => DesignEditors
TDataModuleCustomModule => DMForm (designide.dcp)
TWinControlCustomModule => WCtlForm (designide.dcp)
TFrameModule => VCLFormContainer (vcldesigner.dcp)
I suppose that for FireMonkey it should be possible in similar way (find fmxdesigner.dcp & check what is inside in Notepad++)
PS. In older Delphi versions there was TDataModuleDesignerCustomModule metaclass instead of TDataModuleCustomModule in unit DMDesigner
PPS. Other existing metaclass names:
TCustomFormCustomModule
TIDESourceModuleCustomModule
No, I don't think this is fully possible.
What I usually do when I have similar needs is to simply install the frame descendant as a component of its own right. But yes, that way you lose a lot of the typical frame behaviour (especially at designtime), e.g. you can no longer manipulate sub-components directly and changes to the frame do no longer automatically propagate to forms that use it at designtime - you have to recompile the runtime package that contains the frame first.
Then again, from an OOP-perspective this isn't too bad. It actually enforces the concept of implementation hiding. You can still expose individual properties and functionality of sub-components via new properties and methods on the frame itself.
procedure TMyFrame.Setstr(const Value: string);
begin
inherited;
Edit1.Text := Fstr;
// Sadly this code won't work and Edit1 won't be updated in designtime.
end;
I think it's because it should not work at design time. You registered TBaseFrame as a custom module, so it is the TBaseFrame's (not it's descendants!!!) properties which should be editable at design time. Delphi IDE knows only about published properties of the class you've registered; it doesn't know anything about any descendants and overrides you've made in your project. To make the code work at design time, you should either include it in the TBaseFrame definition:
procedure TBASEFrame.Setstr(const Value: string);
begin
inherited;
Edit1.Text := Fstr;
end;
or (in addition to TBaseFrame) register the TMyFrame definition as a custom module.
Try to understand: Delphi IDE at design time knows only about things that have been registered in it. It's not a handicap; it's logical behaviour.
来源:https://stackoverflow.com/questions/289672/showing-tframe-descendants-additional-properties-on-the-object-inspector