Rai*_*ith 4 delphi components communication frames
最初的问题首先是如何访问组件,但我设法弄清楚了。我刚刚学习 Delphi,所以我很容易遇到愚蠢而明显的问题。我也正处于这样一个阶段:我实际上并没有写任何有用的东西,而只是乱搞一些随机的东西,看看它是如何工作的,也许还能学到一些东西。
文字墙即将到来,我想解释一下我目前正在探索的内容......
基本上我有一个带有按钮1的form1,按下它会创建一个框架2,该框架2有一个按钮2,按下按钮2会在框架2内创建一个框架3(它是框架3的父级和所有者)。每个框架都有另一个 freeandnil 按钮。按下每个按钮 1/2/3 后,它会被禁用以防止创建多个实例。我最初的问题是,在使用 freeandnil-button 之后,我无法访问前一帧上的按钮(它对于表单工作正常,form1.button1.enabled:=true在框架 2 中工作得很好),该按钮被禁用以重新启用它(frame2.button1.enabled:=true从框架 3 中创建一个访问我认为是违规的)。
假设我以后写一些东西需要这样的交流呢?因此,我向每个框架添加了一个编辑框,另一个框架上有一个按钮来更改编辑框文本,这是我当前的工作解决方案:
procedure TFrame2.Button3Click(Sender: TObject);
var i,z:integer;
begin
for i := 0 to ComponentCount - 1 do
if components[i] is tframe3 then
for z := 0 to (components[i] as tframe3).ComponentCount - 1 do
if (components[i] as tframe3).Components[z] is TEdit then
((components[i] as tframe3).Components[z] as TEdit).Text:='ping';
end;
Run Code Online (Sandbox Code Playgroud)
和
procedure TFrame3.Button3Click(Sender: TObject);
var i:integer;
begin
for i := 0 to parent.ComponentCount-1 do
if parent.components[i] is TEdit then
(parent.Components[i] as TEdit).Text:='pong';
end;
Run Code Online (Sandbox Code Playgroud)
如果我有一堆编辑框或其他什么东西,我想我可以使用 Tag 属性来识别它们,但是,这么多的组件计数和传递something AS something对我来说看起来并不正确或足够高效。
我现在的问题是:可以用更好的方式来完成吗?有人可以提供为什么我不能以愚蠢的方式从“子框架”访问“父框架”组件的原因(即:frame2.button1.enabled:=true从frame3内)?
有几点:
控件/组件通常设置为由控制其生命周期的表单/框架拥有,并作为显示它们的控件的父级。因此,当您创建一个位于 TForm 上的 TPanel 上的 TEdit 时,您可以将 TForm 设置为所有者,将 TPanel 设置为 TEdit 的父级。对于框架,您也可以执行相同的操作:框架是其上所有控件的所有者,控件的父级是框架上的任何容器(可以是框架本身)所持有的,因此负责显示(绘制)它。
当您迭代控件的子级时,迭代组件会使用所有者关系;迭代 Controls 使用 Parent 关系。因此,如果要迭代控件,上面的代码就会少得多。
当然,您可以以“哑”方式引用其他控件,但您必须提供访问方法。如果您想要其他控件(而不仅仅是子框架),您至少必须声明一个方法来检索该控件。有很多方法可以做到这一点。一种是在需要时“询问”表单(使用表单上的方法),或者让表单在创建时在框架上设置属性......
避免访问表单和框架的已发布属性。它们主要用于 Delphi 的流系统。当您使用上述方法将应用程序捆绑在一起时,它很快就会变成一团令人难以置信的混乱......
示例即将出现(今晚的某个时候),我需要一些时间来解释,而且我还有工作要做......
以下示例仅处理帧之间的乒乓球比赛。将任何表单或框架从其自己的事件处理程序之一中释放并不是一个好主意。使用Release该方法,因为它可以防止表单/框架在释放后处理消息。(顺便说一句,关于这个问题有很多问题)。另外,当您确实从一个框架自己的按钮释放框架时,您需要注意创建它的框架有机会清零它可能持有的对该框架的引用,否则您正在为自己设置一些有趣的东西调试混乱。查看“Notification”和“NotifyControls”以及发送给其所有者/父级的表单和框架的自动通知,以便这些可以从其组件/控件集合中删除控件。在您的示例中,如果您要从其自己的“FreeAndNil”按钮的 OnClick 事件处理程序中释放 Frame3,则必须确保 Frame2 响应删除(我认为)通知消息,并且它保存的对 Frame3 的任何引用为零(除了那些已经在组件/控件集合中自动清除的)。
现在,乒乓球比赛。有几种方法可以解决这个问题。
第一种方法是您已经尝试过的对另一个框架的组件进行循环的方法。虽然这确实是避免“使用”另一个框架的一种方法,但它很麻烦而且不太简洁。另外,当您在表单/框架上获得更多控件时,您将必须添加对名称的检查才能知道您拥有正确的 TEdit。然后您也可以直接使用该名称,特别是当一个框架的 use 子句中已经有另一个框架时,因为它正在创建它。
// PingFrame (your Frame2)
uses
...
Pong_fr;
type
TPingFrame = class(TFrame)
...
procedure CreateChildBtnClick(Sender: TObject);
procedure PingPongBtnClick(Sender: TObject);
private
FPong: TPongFrame; // This is the "extra" reference you need to nil when
// freeing the TPongFrame from one of its own event handlers.
...
end;
procedure TPingFrame.CreateChildBtnClick(Sender: TObject);
begin
CreateChildBtn.Enabled := False;
FPong := TPongFrame.Create(Self);
FPong.Parent := ContainerPnl;
FPong.Align := alClient;
end;
procedure TPingFrame.PingPongBtnClick(Sender: TObject);
begin
if Assigned(FPong) then
FPong.Edit1.Text := 'Ping';
end;
Run Code Online (Sandbox Code Playgroud)
在另一端:
// PongFrame (your Frame3)
type
TPongFrame = class(TFrame)
...
procedure PingPongBtnClick(Sender: TObject);
end;
implementation
uses
Ping_fr;
procedure TPongFrame.PingPong1BtnClick(Sender: TObject);
begin
(Owner as TPingFrame).Edit1.Text := 'Pong called';
end;
Run Code Online (Sandbox Code Playgroud)
这种方法看起来很漂亮,但也有缺点:
减少框架之间的耦合并允许每个框架根据需要更改其控件的一种方法是不直接使用另一个窗体/框架的控件。为此,您需要在每个框架上声明另一个框架可以调用的方法。每种方法都会更新自己的控件。从 OnClick 事件处理程序中,您不再直接访问其他框架的控件,而是调用该方法
type
TPingFrame = class(TFrame)
...
public
procedure Ping;
end;
implementation
procedure TPingFrame.PingPongBtnClick(Sender: TObject);
begin
if Assigned(FPong) then
FPong.Ping;
end;
procedure TPingFrame.Ping;
begin
Edit1.Text := 'Someone pinged me';
end;
Run Code Online (Sandbox Code Playgroud)
在另一端:
type
TPongFrame = class(TFrame)
...
public
procedure Ping;
end;
implementation
procedure TPongFrame.PingPongBtnClick(Sender: TObject);
begin
(Owner as TPingFrame).Ping;
end;
procedure TPongFrame.Ping;
begin
Edit1.Text := 'Someone pinged me';
end;
Run Code Online (Sandbox Code Playgroud)
这比方法 1 更好,因为它允许两个框架更改其控件,而不必担心“外部”引用它们,但仍然存在循环引用的缺点,只能通过将一个“使用”移至实现来“解决”部分。
最好尝试完全避免循环引用,而不是通过将单元移动到实现部分的使用子句来修补它们。我使用的经验法则是:
任何表单/框架都可以知道并使用它实例化的表单/框架的公共接口(尽管它应该避免该接口的“默认可见性”部分中的控件),但是任何表单/框架都不应该知道特定的创建它的表单/框架。
实现此目的的一种方法(有很多)是使用类似于 TButton 的 OnClick 事件的事件。
在 TPongFrame 方面,您可以删除 Ping_fr 的使用。你不再需要它了。
然后,您需要声明一个属性及其引用的字段,并声明一个触发事件的方法。
type
TPongFrame = class(TFrame)
private
FOnPingPongClicked: TNotifyEvent;
protected
procedure DoPingPongClicked;
public
property OnPingPongClicked: TNotifyEvent
read FOnPingPongClicked write FOnPingPongClicked;
end;
Run Code Online (Sandbox Code Playgroud)
Ping 方法保留,其实现也没有改变。PingPongBtnClick 事件处理程序也保留,但其实现现在变为:
procedure TPongFrame.PingPongBtnClick(Sender: TObject);
begin
DoPingPongClicked;
end;
procedure TPongFrame.DoPingPongClicked;
begin
if Assigned(FOnPingPongClicked) then
FOnPingPongClicked(Self);
end;
Run Code Online (Sandbox Code Playgroud)
您可以将 DoPingPongClicked 中的代码直接放入此处,但最好在单独的方法中触发事件。如果您有一个可以从代码的多个部分触发的事件,它可以避免重复完全相同的代码。它还允许后代(当您获得这些方法时)覆盖“触发”方法(您必须在祖先中将其标记为虚拟)并在事件触发时执行特定的操作。
在 TPingFrame 方面,您需要为新事件编写处理程序:
type
TPingFrame = class(TFrame)
...
protected
procedure HandleOnPingPongClicked(Sender: TObject);
Run Code Online (Sandbox Code Playgroud)
请注意,此处理程序的签名需要是 TNotifyEvent 指定的签名。当然,您需要确保在 TPongFrame 中触发事件时调用此事件处理程序:
procedure TPingFrame.CreateChildBtnClick(Sender: TObject);
begin
CreateChildBtn.Enabled := False;
FPong := TPongFrame.Create(Self);
FPong.Parent := ContainerPnl;
FPong.Align := alClient;
// Listen to event fired by PongFrame whenever it's PingPingBtn is clicked
FPong.OnPingPongClicked := HandleOnPingPongClicked;
end;
Run Code Online (Sandbox Code Playgroud)
在处理程序代码中,您可以执行您需要执行的操作。它可以是一般的:
procedure TPingFrame.HandleOnPingPongClicked(Sender: TObject);
begin
Edit1.Text := 'OnPingPongClicked event was fired';
end;
Run Code Online (Sandbox Code Playgroud)
当然,您还可以使用事件也传递对触发事件的实例的引用这一事实:
procedure TPingFrame.HandleOnPingPongClicked(Sender: TObject);
var
Pong: TPongFrame;
begin
// This checks that Sender actually is a TPongFrame and throws an exception if not
Pong := Sender as TPongFrame;
// Use properties from the Pong instance that was passed in
Edit1.Text := 'OnPingPongClicked event was fired by ' + Pong.Name;
end;
Run Code Online (Sandbox Code Playgroud)
享受!
| 归档时间: |
|
| 查看次数: |
2369 次 |
| 最近记录: |