Как я могу получить доступ к элементу управления delphi снаружи модуля формы? - PullRequest
3 голосов
/ 13 декабря 2010

Я пытаюсь вызвать свойство Enabled таймера из процедуры, определенной следующим образом: procedure Slide(Form: TForm; Show: Boolean); и не с фиксированным именем формы (например: Form2.Timer...)

После помещения модуля формы в список использований это работает: Form2.Timer1.Enabled := True; но следующее не работает: Form.Timer1.Enabled := True; (где Форма - это форма, переданная в качестве параметра в процедуру.

Как получить доступ к компоненту Таймер в моей форме?

Заранее спасибо.

Ответы [ 7 ]

6 голосов
/ 13 декабря 2010

Если каждая форма, которую вы собираетесь передать в свою функцию, будет иметь опубликованное поле с именем «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;

Это довольно слабый интерфейс для программирования. Если вы случайно не указали таймер в вашей форме, или вы дали ему неправильное имя, или если вы дали ему другую видимость, вы не обнаружите свою ошибку до времени выполнения (когда утверждения потерпеть поражение). И даже если форма соответствует требуемому интерфейсу, нет гарантии, что он был намеренным. Может быть много форм, которые опубликовали TTimer поля с именем Timer1, но не все они предназначены для использования с этой функцией Slide. Возможно, они уже используют свои таймеры для других целей, поэтому вызов Slide для них может сломать другие части вашей программы, возможно, трудными для отладки способами.

Было бы немного более сильным интерфейсом, если бы вы дали таймеру более описательное имя, такое как SlideTimer. Timer1 просто говорит, что это был первый TTimer, который вы создали в этой форме, и как только вы покидаете конструктор форм, он перестает быть значимым обозначением. Вы не обязаны использовать варианты именования в среде IDE.


Другой, более лучший вариант - сделать так, чтобы все скользящие формы имели общий базовый класс, и поместили таймер в этот базовый класс. Затем измените тип параметра в Slide, чтобы взять этот класс формы, а не просто TForm.

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

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

Вы, похоже, обеспокоены тем, что вам придется включить ссылку на устройство Form2 в ваше устройство Slide, и это, как правило, полезно. Вы не хотите, чтобы ваш код был слишком тесно связан, так как эта функция 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, то вы можете воспользоваться предложением Дэвида о создании таймера внутри самой подпрограммы и вообще не иметь дело с вызывающим абонентом или формой:

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;
4 голосов
/ 13 декабря 2010

Вы не можете получить доступ к 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;

Или вы позволяете вашей Форме наследовать интерфейс с методами для включения таймера.Хотя это немного сложнее.

4 голосов
/ 13 декабря 2010

Добавить устройство в список использований.

...
implementation

uses Unit2;

{$R *.dfm}
...
2 голосов
/ 13 декабря 2010

Просто чтобы добавить больше информации.

В проекте 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 нет таймера 1.

Вы можете сделать следующее:

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

Если TForm2 - это форма, содержащая таймер.

2 голосов
/ 13 декабря 2010

Использовать имя единицы измерения Form2 в списке использованных единиц измерения.

Для вашего редактирования:

Используя TForm, вы не можете получить доступ к TTimer, потому что TForm имеет no fields or properties as TTimer.

Так что вам нужно использовать TForm1 as the parameter.

Если у вас есть форма, скажем, Form1, для которой вы создаете несколько экземпляров и показываете ее пользователю, измените синтаксис процедуры на procedure Slide(Form: TForm1; Show: Boolean);

Но если у вас несколько форм с разными компонентами, это станет трудным. Вам нужно перегружать процедуры разными параметрами. Ниже код показывает подход.

Форма 1 Единица

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 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.
1 голос
/ 13 декабря 2010

Самый простой способ - найти его в форме:

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;♦♠
0 голосов
/ 14 декабря 2010

Простой (и ООП) способ реализовать это - использовать интерфейсы.

Объявить интерфейс ISlideable, который определяет свойство SlideTimer (с методами getter и setter), а затем написать метод Slide, например

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

И каждая форма, которая должна быть передана, говорит, что она скользящая

MyFormN = class(TForm, ISlideable)
...
...