In a runtime only package, I've defined a TFrame
descendant which publishes the OnLoaded
event:
type
TMyMethod = procedure() of object;
TMyFrame = class(TFrame)
protected
FOnLoaded : TMyMethod;
procedure Loaded(); override;
published
property OnLoaded : TMyMethod read FOnLoaded write FOnLoaded;
end;
implementation
{$R *.dfm}
procedure TMyFrame.Loaded();
begin
inherited;
if(Assigned(FOnLoaded))
then FOnLoaded();
end;
In a designtime only package, I've registered TMyFrame
component as follows:
unit uMyRegistrations;
interface
uses
Classes, uMyFrame;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('MyTestComponents', [
TMyFrame
]);
end;
I've installed the designtime package, I can find TMyFrame
in the tool palette and its OnLoaded
event is shown in the object inspector.
I've dragged a TMyFrame
into a form, then I've assigned the OnLoaded
event by doubleclicking from the object inspector.
After assigning the event, I noticed that an access violation error message appears each time I try to open the form's file in Delphi (It let me open the ".pas" file, but I can't switch to visual designer view).
Did I correctly published the OnLoaded
event? If so, what else is wrong?
Further Informations:
TFrame
descendants).You accepted my original answer, but what I wrote was not correct. Rob Kennedy pointed to an article by former Embarcadero developer Allen Bauer on the topic of Assigned
.
Allen explains that the Assigned
function only tests one pointer of the two pointers in a method pointer. The IDE at design time takes advantage of this by assigning sentinel values to any published method properties (i.e. events). These sentinel values have nil
for one of the two pointers in the method pointer (the one that Assigned
checks), and an index identifying the property value in the other pointer.
All this means that False
is returned when you call Assigned
at design time. So long as you check published method pointers with Assigned
before calling them, then you will never call them at design time.
So what I originally wrote cannot be true.
So I dug a bit deeper. I used the following very simple code, testing with XE7:
type
TMyControl = class(TGraphicControl)
protected
FSize: Integer;
procedure Loaded; override;
end;
....
procedure TMyControl.Loaded;
begin
inherited;
FSize := InstanceSize;
end;
....
procedure Register;
begin
RegisterComponents('MyTestComponents', [TMyControl]);
end;
This was enough to cause an AV in the IDE at design time whenever the Loaded
method was executed.
My conclusion is that the IDE does some rather underhand things when streaming, and your objects are not in a fit state to use when the Loaded
method is called. But I don't really have a better understanding than that.
You must not execute event handlers at design time, and your code does just that. The reason being that at design time the event handler's code is not available.
The control's code is available, the IDE has loaded it – but the code that implements the event handler is not. That code is not part of the design time package, it is part of the project that is currently open in the IDE. After all, it might not even compile yet!
The Loaded
method should defend against this like so:
procedure TMyFrame.Loaded();
begin
inherited;
if not (csDesigning in ComponentState) and Assigned(FOnLoaded) then
FOnLoaded();
end;