我编写了一个基于MDI的应用程序,其中子表单的类型不同。我现在遇到一种情况,我需要一个子表单将消息发送到另一个子表单,告诉它自己更新。第一个子表单不知道此时是否正在显示第二个子表单。
我曾想过让第一个子表单(表单A)向主MDI表单(表单0)发送一条消息,然后可以检查当前正在屏幕上显示的MDI子表单列表。如果正在显示所需的表格(表格B),那么主表格可以向该表格发送第二条消息(表格B)。
不幸的是,我无法成功编写能够使第一个子表单发信号通知主表单的代码。儿童表格如何向其所有者发送信息?
TIA, No'am
答案 0 :(得分:4)
表单的所有者不一定是另一种形式。 Owner属性只是TComponent,可以是任何东西,包括nil 。但如果所有者 表单,您可以向其发送如下消息:
if Owner is TForm then
SendMessage(TForm(Owner).Handle, am_Foo, 0, 0);
但您可能不需要了解所有者。 MDI父表单始终是项目的主要表单,主表单始终由Application.MainForm指定。发送消息到该表单的句柄。
SendMessage(Application.MainForm.Handle, am_Foo, 0, 0);
MDI儿童列表将位于Application.MainForm.MDIChildren。您的子表单可以自己检查该列表,而不是让MDI父表单执行此操作。这是一个函数,您的表单可以用来查找任何MDI子类的实例。 (如果要与通信的表单不是 MDI子项,您仍然可以使用此技术,而不是Application.MainForm.MDIChildren,搜索Screen.Forms列表。)
function FindMDIChild(ChildClass: TFormClass): TForm;
var
i: Integer;
begin
for i := 0 to Pred(Application.MainForm.MDIChildCount) do begin
if Application.MainForm.MDIChild[i].InheritsFrom(ChildClass) then begin
Result := Application.MainForm.MDIChildren[i];
exit;
end;
end;
Result := nil;
end;
你的第一个孩子班可以这样使用它:
var
SecondChild: TForm;
begin
SecondChild := FindMDIChild(TSecondChild);
if Assigned(SecondChild) then begin
SendMessage(SecondChild.Handle, am_Foo, 0, 0);
end;
end;
当窗口消息发送到与发件人相同的线程中的窗口时(任何两个VCL表单都是如此),当发件人等待响应时,会立即调用其处理程序。这就像普通的函数调用一样,所以你可能希望跳过消息并在表单类中创建常规函数。然后你可以使用这样的代码:
var
SecondForm: TSecondForm;
begin
SecondForm := TSecondForm(FindMDIChild(TSecondForm));
if Assigned(SecondForm) then begin
SecondForm.Foo(0, 0);
end;
end;
答案 1 :(得分:2)
这里值得使用的另一种方法是使用接口而不是消息。优点是接口更具体......消息很容易被意外重复使用,除非您非常具体地了解消息常量所在的位置。
要使用此模型,首先要创建一个全局单元(或添加到现有)以下接口声明:
type
ISpecificSignal = interface
{type CTRL-SHIFT-G here to generate a new guid}
procedure PerformSignal(Handle:Integer);
end;
然后修改MAIN表单界面,添加以下内容:
TYPE
TMainForm = Class(TForm,ISpecificSignal)
:
private
Procedure PerformSignal(Handle:Integer);
end;
并且在PerformSignal过程的实现中如下所示:
Procedure TMainForm.PerformSignal(Handle:Integer);
var
i: Integer;
Intf : ISpecificSignal;
begin
for i := 0 to Pred(Application.MainForm.MDIChildCount) do begin
if Supports(Application.MainForm.MDIChild[i],ISpecificSignal,Intf) and
(Application.MainForm.MDIChild[i].Handle <> Handle) then
Intf.PerformSignal(Handle);
end;
在最终必须处理信号的子表单中,执行与主表单相同的步骤,但更改PerformSignal以调用所需的代码。根据需要重复。
在需要实际启动流程的表单中添加以下代码:
procedure DoSomething;
var
Intf : ISpecificSignal;
begin
if Supports(Application.MainForm,ISpecificSignal,Intf) then
Intf.PerformSignal(Handle);
end;
这种方法的最大优点是您不限于传递的参数(接口可以包含任意数量的参数,或者没有参数),并且无需使用消息泵即可运行。
编辑添加了句柄,以避免现有表单也需要来自其他表单的相同通知。
答案 2 :(得分:2)
我不知道您的具体问题,因此这可能不适用于您的情况。
我猜你的情况是FormA编辑一些影响FormB“渲染”的值。我通常处理这种情况的方法是通过使值更改来触发事件。如果需要修改系统中的多个组件,我会使用“多播”事件。
简单的多机构机制如下所示。
TMultiCastNotifyEventReceiver = class(TComponent)
private
FEvent : TNotifyEvent
public
property Event : TNotifyEvent read FEvent write FEvent;
end;
TMultiCastNotifyEvent = class(TComponent)
private
//TList or TObjectList to keep a list of Listener.
//Housekeeping code to make sure you don't keep reference to dangling pointers (I derived from TComponent to have access to the FreeNotification mechanis
public
procedure DoEvent(Sender : Tobject); //Same parameters as TNotifyEvent
function AddListener(NotifyEvent : TNotifyEvent) : TMultiCastNotifyEventReceiver
end;
这样,你的formA不需要知道它的父...不需要知道FormB。 FormB不需要知道FormA。要求它的工作原理是FormA和FormB必须知道 Value ,而 Value 需要知道它需要在修改时发送通知。通常会导致更好的模块化和封装。
然后,我对你试图解决的问题的性质做了很多假设。无论如何,我希望这会有所帮助。
答案 3 :(得分:0)
为什么不将消息发送到Application.Mainform.Handle,然后在Main表单循环中从0到MDIChildcount并将消息重新发送到每个消息。然后,仅在您想要的表单类中响应特定消息。我希望这符合您的需求。