如何创建一个可与 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 项目。
所以你可以采用这种方法:
- 创建您自己的跨平台计时器class。
- 在 Windows 以外的平台上,在 FMX 计时器之上实现它。
- 在 Windows 上使用原始 Windows API 函数
SetTimer
和 KillTimer
实现它。
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;
我有一个需要计时器的 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 项目。
所以你可以采用这种方法:
- 创建您自己的跨平台计时器class。
- 在 Windows 以外的平台上,在 FMX 计时器之上实现它。
- 在 Windows 上使用原始 Windows API 函数
SetTimer
和KillTimer
实现它。
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;