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;