Reputation: 6053
I have a class that requires a timer. The class must work with both VCL and FMX. Unfortunately, the FMX timer is declared in FMX.Types
and the VCL timer in Vcl.ExtCtrls
.
As there is no conditional define like {$IFDEF FMX}xxx{$ENDIF}
how do I use a timer in a cross platform class?
Upvotes: 2
Views: 2011
Reputation: 5566
TTimer
is a component and thus supports interfaces. I use an interposer class to inject an ITimer interface to the original timer class and program only against this interface (which is common for both TTimer
classes).
unit Mv.TimerIntf;
interface
uses
System.Classes;
type
ITimer = interface
function PropGetEnabled: Boolean;
function PropGetInterval: Cardinal;
function PropGetOnTimer: TNotifyEvent;
procedure PropSetEnabled(AValue: Boolean);
procedure PropSetInterval(AValue: Cardinal);
procedure PropSetOnTimer(AValue: TNotifyEvent);
property Enabled: Boolean read PropGetEnabled write PropSetEnabled;
property Interval: Cardinal read PropGetInterval write PropSetInterval;
property OnTimer: TNotifyEvent read PropGetOnTimer write PropSetOnTimer;
end;
implementation
end.
and
unit Mv.VCL.Interposer.Timer;
interface
uses
Mv.TimerIntf,
System.Classes,
VCL.ExtCtrls;
type
TTimer = class(VCL.ExtCtrls.TTimer, ITimer)
function PropGetEnabled: Boolean;
function PropGetInterval: Cardinal;
function PropGetOnTimer: TNotifyEvent;
procedure PropSetEnabled(AValue: Boolean);
procedure PropSetInterval(AValue: Cardinal);
procedure PropSetOnTimer(AValue: TNotifyEvent);
end;
implementation
function TTimer.PropGetEnabled: Boolean;
begin
Result := Enabled;
end;
function TTimer.PropGetInterval: Cardinal;
begin
Result := Interval;
end;
function TTimer.PropGetOnTimer: TNotifyEvent;
begin
Result := OnTimer;
end;
procedure TTimer.PropSetEnabled(AValue: Boolean);
begin
Enabled := AValue;
end;
procedure TTimer.PropSetInterval(AValue: Cardinal);
begin
Interval := AValue;
end;
procedure TTimer.PropSetOnTimer(AValue: TNotifyEvent);
begin
OnTimer := AValue;
end;
end.
...
Usage: Code against the interface:
procedure InitTimer(ATimer: ITimer)
begin
ATimer.Interval := 100;
ATimer.OnTimer := DoWhatEver;
end;
...
uses
Vcl.ExtCtrls,
Mv.VCL.Interposer.Timer;
type
TForm1 = class(TForm)
Timer1: TTimer;
end;
//...
begin
InitTimer(Timer1);
end;
Upvotes: 1
Reputation: 613422
If it was me I would write a dedicated cross platform timer class that was independent of the FMX and VCL frameworks. Conceptually it would sit at the same level as the Delphi RTL.
If you don't want to do that and want to re-use the existing timer classes then you are in a bind. For targets which don't have the VCL, what do you do? There's no way for you to know whether your code will be consumed by an FMX or VCL project. Think about it. You can compile your unit to a .dcu and include it in any project. At the time when the unit is compiled it cannot know the type of project that will ultimately consume it.
So what to do? You could use the FMX timer everywhere. But that forces FMX onto VCL projects. I know I wouldn't like that. You could use the FMX timer everywhere other than Windows and use the VCL timer there. But that forces the VCL onto Windows FMX projects.
So you might take this approach:
SetTimer
and KillTimer
. Upvotes: 4
Reputation: 31443
We can assume that your class is non-visual and that, further, it is not a design-time component. If this were not the case then it would not be otherwise already compatible with both FMX and VCL.
With that being the case, there is no reason you can't include FMX.Types
in a VCL application and there is further no reason that you can't create an FMX.Types.TTimer
in a VCL application - you simply can't do it at design time (ie: drop an FMX TTimer
on a VCL form). If you only need the timer internally, then the answer is clear - just use an FMX timer since it will compile no matter the platform target or the framework being used.
unit FMXTimerInVCLApplication;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
FMX.Types;
type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
private
FTimer : TTimer; // FMX.Types.TTimer !
procedure foo(Sender : TObject);
end;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
FTimer := TTimer.Create(nil);
FTimer.Interval := 1000;
FTimer.OnTimer := foo;
end;
procedure TForm1.foo(Sender : TObject);
begin
ShowMessage('foo');
end;
end.
This does bring a fair bit of FMX baggage into your application, of course. It's a lot of bloat for a timer, if you care about that sort of thing. I present it as an alternative to the other natural answer (which is writing your own).
Upvotes: 1