如何从窗体单元外部访问 Delphi 控件?

发布于 2024-10-07 08:13:20 字数 329 浏览 2 评论 0原文

我试图从如下定义的过程中调用计时器的 Enabled 属性: procedure Slide(Form: TForm; Show: Boolean); 并且不使用固定的表单名称(例如: Form2.Timer...

将表单的单位放入使用列表中后,此方法有效:Form2.Timer1.Enabled := True; 但以下内容不起作用: Form.Timer1.Enabled := True; (其中 Form 是作为参数传递给过程的表单。

如何访问我的表单上的计时器组件?

谢谢进步。

I'm trying to call the Enabled property of a Timer from a procedure defined like this: procedure Slide(Form: TForm; Show: Boolean); and not with a fixed form name (like: Form2.Timer...)

After putting the form's unit in the uses list, this works: Form2.Timer1.Enabled := True;
but the following is not working: Form.Timer1.Enabled := True; (where Form is the form passed as parameter to the procedure.

How to get access to the Timer component on my form?

Thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

苹果你个爱泡泡 2024-10-14 08:13:20

如果您要传递到函数中的每个表单都有一个名为“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;

不过,这是一个相当弱的编程接口。如果您不小心忽略了在表单上放置计时器,或者您给了它错误的名称,或者您给了它不同的可见性,那么直到运行时(当断言失败)。即使表单确实满足所需的接口,也不能保证它是故意的。可能有很多表单发布了名为 Timer1TTimer 字段,但它们并不都适合与此 Slide 函数一起使用。它们可能已经将计时器用于其他目的,因此对它们调用 Slide 会破坏程序的其他部分,而且可能会以难以调试的方式破坏。

如果您为计时器指定一个更具描述性的名称(例如 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 的第二个建议< /a> 是我第一个的变体,但还有另一个变体。您可以传递对组件本身的引用,而不是传递计时器组件的名称

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;

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:

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. 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.

It would be a slightly stronger interface if you gave the timer a more descriptive name, such as 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. You are not required to use the IDE's naming choices.


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.

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. 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. But if it's too loose, then you run into the problems I described above. This TSlidableForm class is a compromise; your Slide function isn't bound directly to TForm2 or its unit. Change TForm2 to descend from TSlidableForm.


The_Fox's second suggestion is a variation on my first, but there's still another variation. 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; 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). 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:

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;
初吻给了烟 2024-10-14 08:13:20

您无法从过程中访问 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;

或者让您的表单继承一个接口方法来打开定时器。但它有点复杂。

You cannot access the Timer from your procedure because your parameter is a TForm, and TForm does not have a Timer1 member. 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.

一江春梦 2024-10-14 08:13:20

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

...
implementation

uses Unit2;

{$R *.dfm}
...

Add the unit to the uses list.

...
implementation

uses Unit2;

{$R *.dfm}
...
弥枳 2024-10-14 08:13:20

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

对于您的编辑:

通过使用 TForm 您无法访问 TTimer,因为 TForm 没有 没有 TTimer 那样的字段或属性。

因此您需要使用TForm1作为参数

如果您有一个表单(例如 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.

Use Form2's unit name in uses list of current unit.

For your Edit:

By using TForm you cant access TTimer because TForm has no fields or properties as TTimer.

So you need to use 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);

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

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses Unit3;

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

Form2 Unit

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.
陈年往事 2024-10-14 08:13:20

只是添加一些更多信息。

在 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 是包含计时器的表单。

Just to add some more information.

In a Delphi project, code is organised into units. Each unit is a file with a name and a .pas extention.

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.

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.

You can do the following:

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

If TForm2 is the form containing the Timer.

听你说爱我 2024-10-14 08:13:20

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

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

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;♦♠
久夏青 2024-10-14 08:13:20

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

声明一个 ISlideable 接口,它定义一个 SlideTimer 属性(带有 getter 和 setter 方法),然后编写 Slide 方法,如下所示

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

并且应该传递的每个表单都表示它是 Slideable

MyFormN = class(TForm, ISlideable)
...

A simple (and OOP) way to implement this is to use interfaces.

Declare an interface ISlideable which defines a SlideTimer property (with getter and setter methods) and then write the Slide method like

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

And every form which should be passed says that it is Slideable

MyFormN = class(TForm, ISlideable)
...
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文