繁体   English   中英

如何从表单单元外部访问delphi控件?

[英]How can I access a delphi control from outside the form's unit?

我试图从这样定义的过程调用Timer的Enabled属性: procedure Slide(Form: TForm; Show: Boolean); 而不是固定的表单名称(如: Form2.Timer...

将表单的单位放在使用列表中后,这可以正常工作: Form2.Timer1.Enabled := True; 但以下是行不通的: Form.Timer1.Enabled := True; (其中Form是作为参数传递给过程的表单。

如何访问表单上的Timer组件?

提前致谢。

如果您要传递给函数的每个表单都有一个名为“Timer1”的已发布字段,那么您可以使用FindComponent方法获取对它的引用:

procedure Slide(Form: TForm; Show: Boolean);
var
  TimerObj: TComponent;
  Timer: TTimer;
begin
  TimerObj := Form.FindComponent('Timer1');
  Assert(Assigned(TimerObj), 'Form has no Timer1');
  Assert(TimerObj is TTimer, 'Form.Timer1 is not a TTimer');
  Timer := TTimer(TimerObj);
  // Continue using Form and Timer
end;

不过,这是一个相当弱的编程接口。 如果你不小心忽略了在你的表单上放置一个计时器,或者你给它一个错误的名字,或者你给它一个不同的可见性,你就不会发现你的错误直到运行时(断言时)失败)。 即使形式符合规定的接口,有没有保证,这是故意的。 可能有许多表单已发布名为Timer1 TTimer字段,但它们并非都适用于此Slide函数。 他们可能已经将他们的计时器用于其他目的,因此调用Slide on会破坏程序的其他部分,可能是难以调试的方式。

如果您为计时器提供了更具描述性的名称,例如SlideTimer ,那么这将是一个稍微强大的界面。 Timer1只是说它是你在该表单上创建的第一个TTimer ,一旦你离开表单设计器,它就不再是一个有意义的名称。 您不需要使用IDE的命名选项。


另一个更好的选择是让所有可滑动的表单都有一个公共基类,并将计时器放在该基类中。 然后,在Slide更改参数类型以获取该表单类而不仅仅是TForm

type
  TSlidableForm = class(TForm)
    Timer1: TTimer;
  end;

procedure Slide(Form: TSlidableForm; Show: Boolean);

你似乎担心你必须在Slide单元中包含对Form2单元的引用,这通常是一个很好的问题。 您不希望代码过于紧密耦合,因为此Slide函数应该能够在一个项目中使用多个表单。 但如果它太松散,那么你会遇到我上面描述的问题。 这个TSlidableForm类是妥协; 您的Slide功能不直接绑定到TForm2或其单位。 TForm2更改为从TSlidableForm下降。


The_Fox的第二个建议是我的第一个变种,但还有另一种变化。 您可以传递对组件本身的引用,而不是传递计时器组件的名称

procedure Slide(Form: TForm; Timer: TTimer; Show: Boolean);

现在您不需要使用FindComponent来搜索要使用的计时器; 你已经提供了它的直接参考。 组件的名称甚至不重要,因此不同的表单可以使用不同的名称。 你可以这样称呼它:

Slide(Form2, Form2.Timer1, True);
Slide(AnotherForm, AnotherForm.SlideTimer, False);

一旦你走到这一步,你可能会超越你原来的问题,并意识到计时器甚至不再需要属于表格。 您可以专门为调用Slide创建它,或者计时器可以完全属于其他东西(如数据模块)。 但是,如果您要为Slide调用创建计时器,那么您可以使用David的建议在例程本身内创建计时器,而不是让调用者或表单处理它:

procedure Slide(Form: TForm; Show: Boolean);
var
  Timer: TTimer;
begin
  Timer := TTimer.Create(nil);
  try
    Timer.OnTimer := ...;
    Timer.Interval := 500;
    Timer.Enabled := True;

    // Put your normal Slide stuff here
  finally
    Timer.Free;
  end;
end;

将单位添加到使用列表中。

...
implementation

uses Unit2;

{$R *.dfm}
...

您无法从您的过程访问Timer,因为您的参数是TForm,而TForm没有Timer1成员。 你必须像这样调整你的程序:

uses
  Unit2; //unit with your form

procedure Slide(Form: TForm2; Show: Boolean); //change TForm2 to the classname you use
begin
  Form.Timer1.Enabled := True;
end;

编辑:

如果你想传递任何形式,你可以试试这个:

procedure Slide(Form: TForm; const aTimerName: string; Show: Boolean);
var
  lComponent: TComponent;
begin
  lComponent := Form.FindComponent(aTimerName);
  if Assigned(lComponent) and (lComponent is TTimer) then
    TTimer(lComponent).Enabled := True;
end;

从表单上的按钮调用如下:

procedure TForm2.Button1Click(Sender: TObject);
begin
  Slide(Self, 'Timer1', False);
end;

或者,让您的表单继承一个接口,其中包含打开计时器的方法。 虽然它有点复杂。

在当前单位的使用列表中使用Form2的单位名称。

对于你的编辑:

通过使用TForm您无法访问TTimer因为TForm no fields or properties as TTimer.

所以你需要使用TForm1 as the parameter

如果您有一个表单Form1,您要为其创建多个实例并向用户显示,然后将您的过程语法更改为procedure Slide(Form: TForm1; Show: Boolean);

但是,如果您有多个具有不同组件的表单,则会变得困难。 您需要使用不同的参数重载过程。 下面的代码显示了该方法。

Form1单位

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses Unit3;

procedure TForm1.Button1Click(Sender: TObject);
begin
Slide(Form1, True)
end;

Form2单位

var
  Form2: TForm2;

implementation

{$R *.dfm}

uses Unit3;

procedure TForm2.Button1Click(Sender: TObject);
begin
Slide(Form2, True)
end;

您的程序所在的单位

unit Unit3;

interface

uses Forms, ExtCtrls, Unit1, Unit2;

procedure Slide(Form: TForm1; Show: Boolean) overload;
procedure Slide(Form: TForm2; Show: Boolean) overload;

implementation


procedure Slide(Form: TForm2; Show: Boolean);
begin
  Form.Timer1.Enabled := True;
end;


procedure Slide(Form: TForm1; Show: Boolean);
begin
  Form.Timer1.Enabled := True;
end;

end.

只是添加更多信息。

在Delphi项目中,代码被组织成单元。 每个单元都是一个名称和.pas扩展名的文件。

该单位具有以下形式:

unit Name;

interface
uses
  // The definition of these units can be used both in the 
  // interface as in the implementation section.
  unit1, unit2;  

// Public interface, visible to other units that use this unit.

implementation
uses
  // The definition of these units can be used only in the 
  // implementation section.
  unit3, unit4;

// Private implementation, not visible outside.

initialization
  // code to initialize the unit, run only once
finalization
  // code to cleanup the unit, run only once
end.

只要在使用之前定义了单位,单位就可以使用单位中定义的任何内容。

如果名称相同,有时这会导致混乱的情况:

unit1;
interface
type
  TTest = Integer;
// ...

unit2;
interface
type
  TTest = Boolean;
// ...

unit3;
interface
uses
  unit1, unit2;
var
  a: TTest;  // Which TTest?
// ...

您可以通过了解评估顺序或使用单位前缀来解决此问题:

unit3;
interface
uses
  unit1, unit2;
var
  a: unit1.TTest;  // TTest from unit1
  b: unit2.TTest;  // TTest from unit2
// ...

在您的情况下,您需要使用定义Form2的单位。

如果两个表单都需要计时器,您还可以使用数据模块(就像表单一样,您可以将不可见的组件拖到它上面,但它们将不可见)。 这两种形式都可以使用数据模块。

编辑

你试着用:

procedure Slide(Form: TForm; Show: Boolean); 

而TForm没有Timer1。

您可以执行以下操作:

procedure Slide(Form: TForm2; Show: Boolean); 

如果TForm2是包含Timer的表单。

最简单的方法是在表单上找到它:

procedure Slide(Form: TForm; Show: Boolean);
var
  Timer: TTimer;
begin
  Timer := Form.FindComponent('Timer1');
  if Assigned(Timer) then
    Timer.Enabled := True;
end;

安抚大卫;-),这是一个更“类型安全”的选择:

procedure Slide(Form: TForm; Show: Boolean);
var
  i:  Integer;
begin
  // Could use a TComponent and for..in instead. This works in
  // all Delphi versions, though.
  for i := 0 to Form.ComponentCount - 1 do
    if Form.Components[i] is TTimer then
      TTimer(Form.Components[i]).Enabled := True;
end;♦♠

实现此目的的简单(和OOP)方法是使用接口。

声明一个接口ISlideable,它定义了一个SlideTimer属性(使用getter和setter方法),然后将Slide方法写成

Slide(const Target: ISlideable; Show: Boolean);

每一个应该传递的形式都说它是可滑动的

MyFormN = class(TForm, ISlideable)
...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM