The problem is that references to interface methods are incompatible with references to object methods.
But instead of directly passing the method reference in any interface implementation, just pass the interface link. On the other side of the fence, instead of referencing the specific method that is being called, you hold a link to an interface that implements this method.
Then you just call the target method at the appropriate time.
In fact, itβs important that you do this in a reference-counting environment, since a link to a specific method on an interface will NOT contribute to link counting on the interface itself .... if you try to save only a link to a method, by the time when your code tries to call this method, the implementation object could be destroyed (since you did not support any reference to it).
If you need to address any aspect of any object that implements an interface, then in the context of reference counting, you must maintain a reference to that interface.
In addition, however, I would suggest sharing the problems. those. separate the fact that your form responds to a timer event from its ability to display:
IfrmMainFormInterface = interface [..guid..] procedure Display; end; ITimerListener = interface [..guid..] procedure OnTimer(Sender: TObject); end; TMainFormViewModel = class strict private fTimer : TTimer; fOnTimer: ITimerListener; procedure DoOnTimer(Sender: TObject); // internal event handler for fTimer.OnTimer public property OnTimer: ITimerListener read fOnTimer write fOnTimer; // no need for getter/setter anymore end; procedure TMainFormViewModel.DoOnTimer(Sender: TObject); begin // Enabling/disabling the timer might not be needed/or appropriate, but if it is // then you can take care of that here, rather than relying on the listener to // do it fTimer.Enabled := FALSE; try if Assigned(fOnTimer) then fOnTimer.OnTimer(self); // call the method on the assigned listener interface finally fTimer.Enabled := TRUE; end; end; // Meanwhile, Somewhere in your view model initialisation.... fTimer.OnTimer := DoOnTimer;
Then in your implementation of TMainForm :
TMainForm = class(TForm, IfrmMainFormInterface, ITimerListener) .. procedure Display; procedure OnTimer(Sender: TObject); .. end; procedure TMainForm.OnTimer(Sender: TObject); begin if Sender is TMainFormViewModel then Display; end;
What do you "attach" to the timer of the view model using the property of the interface type, directly assigning the main form (which will lead to the transfer of a link to the corresponding type):
ViewModel.OnTimer := frmMain;
You may have noticed that in the above example, the view model passes the βIβ as the Sender of the OnTimer call to the listener interface, and not through the initiating timer object. This is to demonstrate how the listener can use the type of the Sender class to (potentially) distinguish between several timer sources that he can listen to.
There are several ways to approach this problem, if it arises, of which this is only one.
Another would be to take advantage of the fact that you now have a special method for listening to the interface for this purpose, separately from the concrete implementation of the main type of event method ( TNotifyEvent ). As a result, you can enter any additional parameters necessary for your timer listener interface method that suit your needs. for example, if your view models have multiple timers, your ITimerListener interface can compress so that in addition to (or instead of) the sender, a timer identifier is passed, for example:
ITimerListener = interface [..guid..] procedure OnTimer(Sender: TObject; aTimerID: Integer); end;