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

emu*_*rad 3 delphi pascal

我试图调用一个计时器从这样的定义的过程Enabled属性:procedure Slide(Form: TForm; Show: Boolean);而不是有固定的形式名称(如:Form2.Timer...)

将表单的单位放入使用列表后,这可以工作:Form2.Timer1.Enabled := True; 但以下操作无效:( Form.Timer1.Enabled := True;其中Form是作为参数传递给过程的表单.

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

提前致谢.

Rob*_*edy 6

如果您要传递给函数的每个表单都有一个名为"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;
Run Code Online (Sandbox Code Playgroud)

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

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


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

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

procedure Slide(Form: TSlidableForm; Show: Boolean);
Run Code Online (Sandbox Code Playgroud)

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


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

procedure Slide(Form: TForm; Timer: TTimer; Show: Boolean);
Run Code Online (Sandbox Code Playgroud)

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

Slide(Form2, Form2.Timer1, True);
Slide(AnotherForm, AnotherForm.SlideTimer, False);
Run Code Online (Sandbox Code Playgroud)

一旦你走到这一步,你可能会超越你原来的问题,并意识到计时器甚至不再需要属于表格.您可以专门为调用创建它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;
Run Code Online (Sandbox Code Playgroud)

  • 以什么方式抛弃这种类型的安全,@大卫?两个版本都检查它们收到的参数是否符合所需的接口.这些技术允许*解耦*,因此`Slide`函数可以使用任何形式,而不仅仅是`Form2`.我更喜欢我的第二个建议,所有形式的共同基类,但这取决于Emurad. (2认同)
  • @Rob你是对的,我的意思不是类型安全,我应该在写作之前仔细考虑一下!我的意思是静态类型而不是类型安全。我仍然认为这是一种相当令人反感的做法。回答所提出的问题并不能真正帮助@emurad,他似乎对编程很陌生。他需要帮助和鼓励来退后一步,以不同的方式看待他的问题。 (2认同)
  • @Sertac在我看来,对于它的价值,专业工程师应该总是质疑正在解决的问题是否实际上是正确的问题. (2认同)