A non-static class method has a Self pointer. That Self is the class. Non-static class methods have a Self in order to support the dispatch of virtual class methods.
Because non-static class methods have Self it means that they can be assigned to of object method types as needed for event handlers. So you can certainly write in Pascal code a runtime assignment:
Button1.OnClick := TForm1.SomeClassMethod;
However, the IDE does not support class methods as event handlers. I believe there is a good reason for this. When the streaming code encounters the setting it looks like this in the .dfm file:
OnClick = SomeClassMethod
This can only appear in the .dfm file when you put it in manually. That's what I think you mean when you say the that the IDE complains. You added the above to the .dfm file and whenever you load the form the IDE rejects it as invalid.
Now, supposing you do this anyway. Then the streaming code encounters the above .dfm setting as text. It needs to use RTTI to find SomeClassMethod. Which is fine. It then synthesises a method value from a data pointer and a code pointer. It uses the instance as the data pointer. And that means that the method is invalid. You cannot pair instance and class method. But the streaming code knows no better and is built on the assumption that the methods found in .dfm files are instance methods. A reasonable assumption given that the designer only offers instance methods.
The code that implements this can be found in TReader.FindMethodInstance:
function TReader.FindMethodInstance(Root: TComponent; const MethodName: string): TMethod;
var
Error: Boolean;
begin
if Assigned(FOnFindMethodInstance) then
begin
Result.Code := Root.MethodAddress(MethodName);
Result.Data := Root;
Error := Result.Code = nil;
FOnFindMethodInstance(Self, MethodName, Result, Error);
end else
Error := True;
if Error then
begin
Result.Data := Root;
Result.Code := FindMethod(Root, MethodName);
end;
end;
The pertinent code is assignment of Root to Result.Data because Root is the instance.
So, when the event fires, your method runs but any attempt to use Self fails. Because the streaming code has given you an invalid Self, an instance instead of a class.
The streaming code cannot properly hook up a class method. And the designer therefore won't offer class methods to use as event handlers. But from code you can perfectly well assign class methods to event handlers and everything works.
Sender, so you could only attach it to a single button (which would have to exist in the first place to be clicked, so there's no reason for it to be aclass procedure).Sender, it's a normal parameter. You cannot access any members ofTForm9but like in this example this is not always necessary. The reason is rather because methods consist of the Data and the code pointer and the Data is always the instance pointer and not as in the case of a class procedure the class pointer.