如何创建一个可与 VCL 和 FMX 一起使用的内部计时器?

How can I create an internal timer that will work with VCL and FMX?

我有一个需要计时器的 class。 class 必须与 VCL 和 FMX 一起工作。不幸的是,FMX 计时器在 FMX.Types 中声明,而 VCL 计时器在 Vcl.ExtCtrls 中声明。

因为没有像{$IFDEF FMX}xxx{$ENDIF}这样的条件定义,我如何在跨平台中使用计时器class?

我们可以假设您的 class 是非可视化的,而且,它不是设计时组件。如果不是这种情况,那么它就不会与 FMX 和 VCL 兼容。

在这种情况下,没有理由不能在 VCL 应用程序中包含 FMX.Types,也没有理由不能在 VCL 中创建 FMX.Types.TTimer应用程序 - 您根本无法在设计时执行此操作(即:将 FMX TTimer 放在 VCL 窗体上)。如果您只需要内部计时器,那么答案很明确 - 只需使用 FMX 计时器,因为无论使用的平台目标或框架如何,它都会编译。

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.

当然,这确实会给您的应用程序带来一些 FMX 包袱。如果您关心这类事情,那么对于计时器来说,这会显得很臃肿。我将其作为其他自然答案(您自己编写)的替代方案。

如果是我,我会编写一个独立于 FMX 和 VCL 框架的专用跨平台计时器 class。从概念上讲,它将与 Delphi RTL 处于同一级别。

如果您不想这样做并想重新使用现有的计时器 classes 那么您将陷入困境。对于没有 VCL 的目标,你会怎么做?您无法知道您的代码是否会被 FMX 或 VCL 项目使用。想想看。您可以将您的单元编译为 .dcu 并将其包含在任何项目中。在编译单元时,它无法知道最终将使用它的项目类型。

那怎么办?您可以在任何地方使用 FMX 计时器。但这迫使 FMX 进入 VCL 项目。我知道我不会喜欢那样。您可以在 Windows 以外的任何地方使用 FMX 计时器,并在那里使用 VCL 计时器。但这迫使 VCL 进入 Windows FMX 项目。

所以你可以采用这种方法:

  1. 创建您自己的跨平台计时器class。
  2. 在 Windows 以外的平台上,在 FMX 计时器之上实现它。
  3. 在 Windows 上使用原始 Windows API 函数 SetTimerKillTimer 实现它。

TTimer 是一个组件,因此支持接口。我使用插入器 class 将 ITimer 接口注入原始定时器 class 并仅针对该接口进行编程(这对 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.

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.

...

用法: 接口代码:

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;