简体   繁体   English

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

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

I'm trying to call the Enabled property of a Timer from a procedure defined like this: procedure Slide(Form: TForm; Show: Boolean); 我试图从这样定义的过程调用Timer的Enabled属性: procedure Slide(Form: TForm; Show: Boolean); and not with a fixed form name (like: Form2.Timer... ) 而不是固定的表单名称(如: Form2.Timer...

After putting the form's unit in the uses list, this works: Form2.Timer1.Enabled := True; 将表单的单位放在使用列表中后,这可以正常工作: Form2.Timer1.Enabled := True; but the following is not working: Form.Timer1.Enabled := True; 但以下是行不通的: Form.Timer1.Enabled := True; (where Form is the form passed as parameter to the procedure. (其中Form是作为参数传递给过程的表单。

How to get access to the Timer component on my form? 如何访问表单上的Timer组件?

Thanks in advance. 提前致谢。

If every form you're going to pass into your function will have a published field named "Timer1," then you can use the FindComponent method to get a reference to it: 如果您要传递给函数的每个表单都有一个名为“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;

That's a rather weak interface to program against, though. 不过,这是一个相当弱的编程接口。 If you've accidentally neglected to place a timer on your form, or if you've given it the wrong name, or if you've given it a different visibility, you won't discover your mistake until run time (when the assertions fail). 如果你不小心忽略了在你的表单上放置一个计时器,或者你给它一个错误的名字,或者你给它一个不同的可见性,你就不会发现你的错误直到运行时(断言时)失败)。 And even if the form does meet the required interface, there's no guarantee that it was intentional. 即使形式符合规定的接口,有没有保证,这是故意的。 There may be lots of forms that have published TTimer fields named Timer1 , but they're not all meant to be used with this Slide function. 可能有许多表单已发布名为Timer1 TTimer字段,但它们并非都适用于此Slide函数。 They might already be using their timers for other purposes, so calling Slide on them will break other parts of your program, possibly in difficult-to-debug ways. 他们可能已经将他们的计时器用于其他目的,因此调用Slide on会破坏程序的其他部分,可能是难以调试的方式。

It would be a slightly stronger interface if you gave the timer a more descriptive name, such as SlideTimer . 如果您为计时器提供了更具描述性的名称,例如SlideTimer ,那么这将是一个稍微强大的界面。 Timer1 merely says it was the first TTimer you created on that form, and once you leave the Form Designer, that ceases to be a meaningful designation. Timer1只是说它是你在该表单上创建的第一个TTimer ,一旦你离开表单设计器,它就不再是一个有意义的名称。 You are not required to use the IDE's naming choices. 您不需要使用IDE的命名选项。


Another, better, option is to make all your slidable forms have a common base class, and put the timer in that base class. 另一个更好的选择是让所有可滑动的表单都有一个公共基类,并将计时器放在该基类中。 Then, change the parameter type in Slide to take that form class instead of just TForm . 然后,在Slide更改参数类型以获取该表单类而不仅仅是TForm

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

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

You seemed concerned that you would have to include a reference to Form2's unit in your Slide unit, and that's generally a good concern to have. 你似乎担心你必须在Slide单元中包含对Form2单元的引用,这通常是一个很好的问题。 You don't want your code to be too tightly coupled since this Slide function should be able to work with more than just one form in one project. 您不希望代码过于紧密耦合,因为此Slide函数应该能够在一个项目中使用多个表单。 But if it's too loose, then you run into the problems I described above. 但如果它太松散,那么你会遇到我上面描述的问题。 This TSlidableForm class is a compromise; 这个TSlidableForm类是妥协; your Slide function isn't bound directly to TForm2 or its unit. 您的Slide功能不直接绑定到TForm2或其单位。 Change TForm2 to descend from TSlidableForm . TForm2更改为从TSlidableForm下降。


The_Fox's second suggestion is a variation on my first, but there's still another variation. The_Fox的第二个建议是我的第一个变种,但还有另一种变化。 Instead of passing the name of the timer component, you can pass a reference to the component itself: 您可以传递对组件本身的引用,而不是传递计时器组件的名称

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

Now you don't need to use FindComponent to search for the timer to use; 现在您不需要使用FindComponent来搜索要使用的计时器; you've provided a direct reference to it. 你已经提供了它的直接参考。 The component's name doesn't even matter, so different forms can use different names. 组件的名称甚至不重要,因此不同的表单可以使用不同的名称。 You can call it like this: 你可以这样称呼它:

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

Once you've come this far, you may go beyond your original question and realize that the timer doesn't even need to belong to the form anymore. 一旦你走到这一步,你可能会超越你原来的问题,并意识到计时器甚至不再需要属于表格。 You could create it specially for the call to Slide , or the timer could belong to something else entirely (like a data module). 您可以专门为调用Slide创建它,或者计时器可以完全属于其他东西(如数据模块)。 But if you're going to create the timer just for the Slide call, then you could use David's suggestion of creating the timer within the routine itself and not have the caller or the form deal with it at all: 但是,如果您要为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;

Add the unit to the uses list. 将单位添加到使用列表中。

...
implementation

uses Unit2;

{$R *.dfm}
...

You cannot access the Timer from your procedure because your parameter is a TForm, and TForm does not have a Timer1 member. 您无法从您的过程访问Timer,因为您的参数是TForm,而TForm没有Timer1成员。 You have to adjust your procedure like this: 你必须像这样调整你的程序:

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;

Edit: 编辑:

If you want to pass any form, you can try this: 如果你想传递任何形式,你可以试试这个:

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;

Call like this from a button on your form: 从表单上的按钮调用如下:

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

Or you let your Form inherit an interface with methods to turn on the timer. 或者,让您的表单继承一个接口,其中包含打开计时器的方法。 It's a little bit more complicated though. 虽然它有点复杂。

Use Form2's unit name in uses list of current unit. 在当前单位的使用列表中使用Form2的单位名称。

For your Edit: 对于你的编辑:

By using TForm you cant access TTimer because TForm has no fields or properties as TTimer. 通过使用TForm您无法访问TTimer因为TForm no fields or properties as TTimer.

So you need to use TForm1 as the parameter . 所以你需要使用TForm1 as the parameter

If you are having a form say Form1, for which you are creating multiple instances and showing to the user, then change your procedure syntax to procedure Slide(Form: TForm1; Show: Boolean); 如果您有一个表单Form1,您要为其创建多个实例并向用户显示,然后将您的过程语法更改为procedure Slide(Form: TForm1; Show: Boolean);

But if you are having multiple forms with different components, it will become difficult. 但是,如果您有多个具有不同组件的表单,则会变得困难。 You need to overload procedures with different parameters. 您需要使用不同的参数重载过程。 Below code shows the approach. 下面的代码显示了该方法。

Form1 Unit Form1单位

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses Unit3;

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

Form2 Unit Form2单位

var
  Form2: TForm2;

implementation

{$R *.dfm}

uses Unit3;

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

Unit in which your procedures lies 您的程序所在的单位

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.

Just to add some more information. 只是添加更多信息。

In a Delphi project, code is organised into units. 在Delphi项目中,代码被组织成单元。 Each unit is a file with a name and a .pas extention. 每个单元都是一个名称和.pas扩展名的文件。

The unit has the following form: 该单位具有以下形式:

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.

A unit can use anything defined in the unit as long as it is defined before it is beïng used. 只要在使用之前定义了单位,单位就可以使用单位中定义的任何内容。

Sometimes this leads to confusing situations if names are identical: 如果名称相同,有时这会导致混乱的情况:

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

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

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

You can solve this by either knowing the evaluation order, or use a unit prefix: 您可以通过了解评估顺序或使用单位前缀来解决此问题:

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

In your case, you need to use the unit in which Form2 is defined. 在您的情况下,您需要使用定义Form2的单位。

If the timer is needed by both forms, you can also use a datamodule (just like a form, you can drag nonvisible components to it but they won't be visible). 如果两个表单都需要计时器,您还可以使用数据模块(就像表单一样,您可以将不可见的组件拖到它上面,但它们将不可见)。 Both forms then can use the datamodule. 这两种形式都可以使用数据模块。

Edit 编辑

You try to use: 你试着用:

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

And TForm has no Timer1. 而TForm没有Timer1。

You can do the following: 您可以执行以下操作:

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

If TForm2 is the form containing the Timer. 如果TForm2是包含Timer的表单。

The easiest way is to find it on the form: 最简单的方法是在表单上找到它:

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

To appease David ;-), here's a more "type-safe" alternative: 安抚大卫;-),这是一个更“类型安全”的选择:

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;♦♠

A simple (and OOP) way to implement this is to use interfaces. 实现此目的的简单(和OOP)方法是使用接口。

Declare an interface ISlideable which defines a SlideTimer property (with getter and setter methods) and then write the Slide method like 声明一个接口ISlideable,它定义了一个SlideTimer属性(使用getter和setter方法),然后将Slide方法写成

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

And every form which should be passed says that it is Slideable 每一个应该传递的形式都说它是可滑动的

MyFormN = class(TForm, ISlideable)
...

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

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