Delphi - 从表单访问Frame对象

时间:2022-12-16 15:54:50

I need to run an action that is attached to a button (say SQLBtn) that is placed on a Frame1 within my app, from Form1.
I have included the frame in Form1 uses, but can't seem to address in any way.
I've tried Frame1.SQLbtn TFrame1.SQLbtn TFrameSQLBtn etc but can't get to it.
I would like to get to something similar to 'SQLbtn.click' to run the event behind it.

我需要运行一个附加到按钮(例如SQLBtn)的动作,该动作放在我的应用程序中的Frame1上,来自Form1。我已经在Form1中使用了框架,但似乎无法以任何方式解决。我已经尝试过Frame1.SQLbtn TFrame1.SQLbtn TFrameSQLBtn等但无法实现它。我想得到类似于'SQLbtn.click'的东西来运行它背后的事件。

Does any one have any ideas how to address it?

有没有人有任何想法如何解决它?

4 个解决方案

#1


I am not sure I understand your question correctly. Sounds like you have a frame with a button (and either an TAction or click event handler on the button) and this frame is sitting on a form. Now you want to programmatically simulate a click on that button.

我不确定我是否理解你的问题。听起来你有一个带按钮的框架(按钮上有一个TAction或click事件处理程序),这个框架就坐在一个表格上。现在,您希望以编程方式模拟该按钮上的单击。

Obviously you need to add the frame unit to your form's uses clause. You also need an instance of the frame on the form which should lead to a form field of the frame type, e.g.

显然,您需要将框架单元添加到窗体的uses子句中。您还需要表单上的框架实例,该实例应该指向框架类型的表单字段,例如

TForm1...
  ...
  Frame1: TFrame1;
end;

Then you can execute that code via Frame1.SQLbtn.Click from within any of the form's methods. A better way would probably be to provide a public method on the frame which you can use from the form. Then you don't need to access the button directly (the button is an implementation detail of the frame, frame private so to speak).

然后,您可以通过任何表单方法中的Frame1.SQLbtn.Click执行该代码。更好的方法可能是在框架上提供可以从表单中使用的公共方法。然后你不需要直接访问按钮(按钮是框架的实现细节,框架私有可以这么说)。

Edit after clarification

澄清后编辑

I understand you have the following scenario:

我知道您有以下情况:

TFrameForm1...
  ...
  Frame1: TFrame1;
end;

TForm1...
  ...
  procedure something;
end;

procedure TForm1.something;
begin
  // how to call a method on Frame1 which is on FrameForm1
end;

Your best choice is to move the code from frame button OnClick event handler into a separate unit. This can be a datamodule, or a just another unit with a standalone procedure. Then you can call that code from both Form1 and the Frame1 button event handler. This is what Vegar has commented.

您最好的选择是将代码从框架按钮OnClick事件处理程序移动到一个单独的单元中。这可以是数据模块,也可以是具有独立过程的另一个单元。然后,您可以从Form1和Frame1按钮事件处理程序中调用该代码。这是Vegar评论的内容。

If that is not possible, e.g. because the processing requires access to other controls on Frame1, move the code into a new procedure on Frame1 (my original suggestion):

如果那是不可能的,例如因为处理需要访问Frame1上的其他控件,将代码移动到Frame1上的新过程(我的原始建议):

TFrame1...
  ...
public
  procedure framestuff;
end;

procedure TFrame1.framestuff;
begin
  ...
end;

procedure TFrame1.SQLbtnClick(Sender...);
begin
  framestuff;
end;

Now you need to call that method from Form1. You'll need a reference to FrameForm1 for that. Which you need to initialize manually (!) when you create TFrameForm1. In this example, the reference is a field FFrameForm:

现在,您需要从Form1调用该方法。你需要一个对FrameForm1的引用。在创建TFrameForm1时需要手动初始化(!)。在此示例中,引用是字段FFrameForm:

TForm1...
  ...
  FFrameForm: TFrameForm1;
  procedure something;      
end;

procedure TForm1.something;
begin
  FrameForm.framestuff;
end;

Or, by default Delphi adds global variables for all forms to the form units (auto form creation, check project options / forms). Then you do this:

或者,默认情况下,Delphi将所有表单的全局变量添加到表单单元(自动表单创建,检查项目选项/表单)。然后你这样做:

procedure TForm1.something;
begin
  FrameForm1.framestuff; // if FrameForm1 is the name Delphi used for the global variable
end;

Of course there are many other variations...

当然还有很多其他的变化......

#2


procedure TDiferentForm.DoSomething();
begin
  Form1.YourFrame.ButtonClick(nil);
end;

#3


One thing that might help you understand: when you create an instance of a form (or frame), delphi goes through the DFM and creates instances of all the objects described there.

有一件事可能有助于您理解:当您创建表单(或框架)的实例时,delphi会通过DFM并创建其中描述的所有对象的实例。

IF you have a variable in the form's definition that matches the name of the object in the DFM, the loader will make the variable point to the object; if you don't have a variable, the object is created but you would have to iterate through .Components or .Controls to get to it.

如果表单定义中的变量与DFM中对象的名称匹配,则加载器将使变量指向对象;如果你没有变量,那么就创建了对象,但你必须遍历.Components或.Controls来获取它。

If the form has an instance variable of the frame (and that variable is public), then any other form's code can access it (e.g. MainForm.Frame1...) and do what it wants to.

如果表单具有框架的实例变量(并且该变量是公共的),则任何其他表单的代码都可以访问它(例如MainForm.Frame1 ...)并执行它想要的操作。

To encapsulate the frame, the form (which is, after all just a class) can have public properties that have accessors and mutators to proxy the information to and from the embedded frame. Encapsulation is good (IMHO the most important aspect of OOP) because it makes the link between the caller and the frame loose: you can change either side a lot without breaking things.

为了封装框架,表单(毕竟只是一个类)可以具有公共属性,这些公共属性具有访问器和更改器以将信息代理到嵌入式框架和从嵌入式框架代理信息。封装是好的(恕我直言是OOP最重要的方面)因为它使调用者和框架之间的链接松散:你可以在不破坏任何东西的情况下改变任何一方。

Cheers

#4


Another solution is to use interfaces to avoid the circular reference problem and simplify the code a bit. Lets say that you have a procedure named foo that you want to invoke from anyplace in the system. The implementation of this procedure is in tFooForm which is not the main form, but a form that the main form knows about.

另一种解决方案是使用接口来避免循环引用问题并稍微简化代码。假设您有一个名为foo的过程,您希望从系统中的任何位置调用该过程。这个过程的实现是在tFooForm中,它不是主要形式,而是主要形式所知道的形式。

First create a new unit and call it Foo_Intf.pas

首先创建一个新单元并将其命名为Foo_Intf.pas

Its contents will be the following:

其内容如下:

unit Foo_Intf;

interface

type
  IFoo = interface
    ['{4AC12AB9-557B-4E61-AB2D-8B10E591E33A}'] 
    // CTRL-SHIFT-G to create a new guid
    procedure Foo;
  end;

implementation

end.

then add the method to the tFooForm class, and also include the interface. Don't forget to use the foo_intf.pas unit in your interface uses clause. Implement the foo class to do what ever you want that procedure to perform.

然后将该方法添加到tFooForm类,并包括该接口。不要忘记在interface uses子句中使用foo_intf.pas单元。实现foo类以执行您希望该过程执行的操作。

tFooForm = class(tForm,IFoo)
  :
  procedure Foo;
  :
end;

Also add the IFoo interface to the main form, exactly like the previous step but change the implementation to be the following:

还要将IFoo接口添加到主窗体,与上一步完全相同,但将实现更改为以下内容:

procedure tMainForm.Foo;
begin
  if not Assigned(FooForm) then
    FooForm := tFooForm.Create(Application);  // for simplicity sake
  FooForm.Foo;
end;

Now, anyplace you want to call the foo function, just include ONLY the Foo_Intf unit in the uses clause and use the following snippit:

现在,您想要调用foo函数的任何地方,只需在uses子句中包含Foo_Intf单元并使用以下snippit:

var
  FooIntf : IFoo;
begin
  if Not Supports(Application.MainForm, IFoo, FooIntf) then
    Raise Exception.create('Application.mainform does not implement IFoo');   
  FooIntf.Foo;
end;

#1


I am not sure I understand your question correctly. Sounds like you have a frame with a button (and either an TAction or click event handler on the button) and this frame is sitting on a form. Now you want to programmatically simulate a click on that button.

我不确定我是否理解你的问题。听起来你有一个带按钮的框架(按钮上有一个TAction或click事件处理程序),这个框架就坐在一个表格上。现在,您希望以编程方式模拟该按钮上的单击。

Obviously you need to add the frame unit to your form's uses clause. You also need an instance of the frame on the form which should lead to a form field of the frame type, e.g.

显然,您需要将框架单元添加到窗体的uses子句中。您还需要表单上的框架实例,该实例应该指向框架类型的表单字段,例如

TForm1...
  ...
  Frame1: TFrame1;
end;

Then you can execute that code via Frame1.SQLbtn.Click from within any of the form's methods. A better way would probably be to provide a public method on the frame which you can use from the form. Then you don't need to access the button directly (the button is an implementation detail of the frame, frame private so to speak).

然后,您可以通过任何表单方法中的Frame1.SQLbtn.Click执行该代码。更好的方法可能是在框架上提供可以从表单中使用的公共方法。然后你不需要直接访问按钮(按钮是框架的实现细节,框架私有可以这么说)。

Edit after clarification

澄清后编辑

I understand you have the following scenario:

我知道您有以下情况:

TFrameForm1...
  ...
  Frame1: TFrame1;
end;

TForm1...
  ...
  procedure something;
end;

procedure TForm1.something;
begin
  // how to call a method on Frame1 which is on FrameForm1
end;

Your best choice is to move the code from frame button OnClick event handler into a separate unit. This can be a datamodule, or a just another unit with a standalone procedure. Then you can call that code from both Form1 and the Frame1 button event handler. This is what Vegar has commented.

您最好的选择是将代码从框架按钮OnClick事件处理程序移动到一个单独的单元中。这可以是数据模块,也可以是具有独立过程的另一个单元。然后,您可以从Form1和Frame1按钮事件处理程序中调用该代码。这是Vegar评论的内容。

If that is not possible, e.g. because the processing requires access to other controls on Frame1, move the code into a new procedure on Frame1 (my original suggestion):

如果那是不可能的,例如因为处理需要访问Frame1上的其他控件,将代码移动到Frame1上的新过程(我的原始建议):

TFrame1...
  ...
public
  procedure framestuff;
end;

procedure TFrame1.framestuff;
begin
  ...
end;

procedure TFrame1.SQLbtnClick(Sender...);
begin
  framestuff;
end;

Now you need to call that method from Form1. You'll need a reference to FrameForm1 for that. Which you need to initialize manually (!) when you create TFrameForm1. In this example, the reference is a field FFrameForm:

现在,您需要从Form1调用该方法。你需要一个对FrameForm1的引用。在创建TFrameForm1时需要手动初始化(!)。在此示例中,引用是字段FFrameForm:

TForm1...
  ...
  FFrameForm: TFrameForm1;
  procedure something;      
end;

procedure TForm1.something;
begin
  FrameForm.framestuff;
end;

Or, by default Delphi adds global variables for all forms to the form units (auto form creation, check project options / forms). Then you do this:

或者,默认情况下,Delphi将所有表单的全局变量添加到表单单元(自动表单创建,检查项目选项/表单)。然后你这样做:

procedure TForm1.something;
begin
  FrameForm1.framestuff; // if FrameForm1 is the name Delphi used for the global variable
end;

Of course there are many other variations...

当然还有很多其他的变化......

#2


procedure TDiferentForm.DoSomething();
begin
  Form1.YourFrame.ButtonClick(nil);
end;

#3


One thing that might help you understand: when you create an instance of a form (or frame), delphi goes through the DFM and creates instances of all the objects described there.

有一件事可能有助于您理解:当您创建表单(或框架)的实例时,delphi会通过DFM并创建其中描述的所有对象的实例。

IF you have a variable in the form's definition that matches the name of the object in the DFM, the loader will make the variable point to the object; if you don't have a variable, the object is created but you would have to iterate through .Components or .Controls to get to it.

如果表单定义中的变量与DFM中对象的名称匹配,则加载器将使变量指向对象;如果你没有变量,那么就创建了对象,但你必须遍历.Components或.Controls来获取它。

If the form has an instance variable of the frame (and that variable is public), then any other form's code can access it (e.g. MainForm.Frame1...) and do what it wants to.

如果表单具有框架的实例变量(并且该变量是公共的),则任何其他表单的代码都可以访问它(例如MainForm.Frame1 ...)并执行它想要的操作。

To encapsulate the frame, the form (which is, after all just a class) can have public properties that have accessors and mutators to proxy the information to and from the embedded frame. Encapsulation is good (IMHO the most important aspect of OOP) because it makes the link between the caller and the frame loose: you can change either side a lot without breaking things.

为了封装框架,表单(毕竟只是一个类)可以具有公共属性,这些公共属性具有访问器和更改器以将信息代理到嵌入式框架和从嵌入式框架代理信息。封装是好的(恕我直言是OOP最重要的方面)因为它使调用者和框架之间的链接松散:你可以在不破坏任何东西的情况下改变任何一方。

Cheers

#4


Another solution is to use interfaces to avoid the circular reference problem and simplify the code a bit. Lets say that you have a procedure named foo that you want to invoke from anyplace in the system. The implementation of this procedure is in tFooForm which is not the main form, but a form that the main form knows about.

另一种解决方案是使用接口来避免循环引用问题并稍微简化代码。假设您有一个名为foo的过程,您希望从系统中的任何位置调用该过程。这个过程的实现是在tFooForm中,它不是主要形式,而是主要形式所知道的形式。

First create a new unit and call it Foo_Intf.pas

首先创建一个新单元并将其命名为Foo_Intf.pas

Its contents will be the following:

其内容如下:

unit Foo_Intf;

interface

type
  IFoo = interface
    ['{4AC12AB9-557B-4E61-AB2D-8B10E591E33A}'] 
    // CTRL-SHIFT-G to create a new guid
    procedure Foo;
  end;

implementation

end.

then add the method to the tFooForm class, and also include the interface. Don't forget to use the foo_intf.pas unit in your interface uses clause. Implement the foo class to do what ever you want that procedure to perform.

然后将该方法添加到tFooForm类,并包括该接口。不要忘记在interface uses子句中使用foo_intf.pas单元。实现foo类以执行您希望该过程执行的操作。

tFooForm = class(tForm,IFoo)
  :
  procedure Foo;
  :
end;

Also add the IFoo interface to the main form, exactly like the previous step but change the implementation to be the following:

还要将IFoo接口添加到主窗体,与上一步完全相同,但将实现更改为以下内容:

procedure tMainForm.Foo;
begin
  if not Assigned(FooForm) then
    FooForm := tFooForm.Create(Application);  // for simplicity sake
  FooForm.Foo;
end;

Now, anyplace you want to call the foo function, just include ONLY the Foo_Intf unit in the uses clause and use the following snippit:

现在,您想要调用foo函数的任何地方,只需在uses子句中包含Foo_Intf单元并使用以下snippit:

var
  FooIntf : IFoo;
begin
  if Not Supports(Application.MainForm, IFoo, FooIntf) then
    Raise Exception.create('Application.mainform does not implement IFoo');   
  FooIntf.Foo;
end;