如何在Delphi中触发Form OnActivate事件?

时间:2018-02-23 05:21:32

标签: delphi events

我必须在应用程序中打开多个表单,并且我使用TtoolButton和TActionlist作为菜单栏。我编写了一个程序来创建/显示每个表单。我很难在此过程中触发Form OnActivate事件。 每个表单都在Tpanel内打开,Tpanel位于FormHome的主要表单中。 我很感激你的帮助!

请参阅Delphi 10.2中的代码

procedure TFormHome.PR_OpenForm(Pform : TFormClass);
var
    vform : TForm;
begin    
           vform              := Pform.Create(Application);
           vform.Parent       := PanelCorpo;
           vform.Align        := alclient;
           vform.BorderIcons  := [biSystemMenu];
           vform.BorderStyle  := bsNone;
           vform.Show;
           vform.SetFocus;
           vform.OnActivate(??); // That is the issue, how to call this event ?      
end;

提前致谢!

**添加免费信息以解释为什么我需要一种方法来创建/打开表单**

这是我用来打开每个特定表单的代码。我有一个方法,每个表单具有完全相同的代码。唯一的区别是Form实例本身:

procedure TFormHome.OpenDiretorioExecute(Sender: TObject);
begin
     if Not Assigned(FormDiretorio) then
     begin               
           FormDiretorio              := TFormDiretorio.Create(Self);
           FormDiretorio.Parent       := PanelCorpo;
           FormDiretorio.Align        := alclient;
           FormDiretorio.BorderIcons  := [biSystemMenu];
           FormDiretorio.BorderStyle  := bsNone;
           FormDiretorio.Show;
           FormDiretorio.SetFocus;
           FormDiretorio.OnActivate(Sender);  // In this way , OnActivate works fine
     end;
end;  

我需要/想要的东西:

我只需要一种方法来打开所有表单。上面编码的TFormHome.PR_OpenForm(Pform:TFormClass)几乎就在那里,除了OnActivate方法不起作用!

你能帮我解决一下吗?

谢谢!

示例代码 - 包含旧代码和新代码的项目 ===>主要表格" FormHome"

... // This is the main Form  FormHOme which calls FormA, FormB and FormC
    // There is a TToolbar with 3 Toolbutton that uses a TActionlist 
    //  FormA and FormB are called by the old style method Action1Execute 
    //  and Action2Execute 
    // FormC is called by the new method PR_CreateOpenForm  , which
   //  presents the error
var
  FormHome: TFormHome;

implementation

uses
    UnitFormA,
    unitFormB,
    UnitFormC;
{$R *.dfm}


procedure TFormHome.Action1Execute(Sender: TObject);
// Action1 : OnExecute event,  called from ToolButton1
begin
     if Not Assigned(FormA) then
     begin
           FormA              := TFormA.Create(Self);
     end;
     FormA.Parent       := Panelhome;
     FormA.Align        := alclient;
     FormA.BorderIcons  := [biSystemMenu];
     FormA.BorderStyle  := bsNone;
     FormA.Show;
     FormA.SetFocus;
     FormA.OnActivate(Sender); // There is a code in OnActivate event in FormA
end;


procedure TFormHome.Action2Execute(Sender: TObject);
// Action2 : OnExecute event , called from ToolButton2
begin
     if Not Assigned(FormB) then
     begin
           FormB              := TFormB.Create(Self);
     end;
     FormB.Parent       := Panelhome;
     FormB.Align        := alclient;
     FormB.BorderIcons  := [biSystemMenu];
     FormB.BorderStyle  := bsNone;
     FormB.Show;
     FormB.SetFocus;
     FormB.OnActivate(Sender); // There is a code in OnActivate event in FormB
end ;


procedure TFormHome.Action3Execute(Sender: TObject);
// Action3 OnExecute event, called from ToolButton3
// This is the desired code to implment in all Action OnExecute event
begin
      PR_CreateOpenForm(TFormC);  // Fails in the  OnActivate event
end;


procedure TFormHome.PR_CreateOpenForm(PClassform : TFormClass);
// This routine should be used to create/open all forms
// 
var
    vform : TForm;
begin
      if Not Assigned(Tform(PClassform)) then
      begin
           vform              := Pclassform.Create(Application);
      end;
           vform.Parent       := PanelHome;
           vform.Align        := alclient;
           vform.BorderIcons  := [biSystemMenu];
           vform.BorderStyle  := bsNone;
           vform.Show;
           vform.SetFocus;
           vform.onActivate(self); // Does not work !! Tried with : vform.Onactivate(nil) - vform.Onactivate(Tform)
end;

end.

FORMA - OnActivate事件

procedure TFormA.FormActivate(Sender: TObject);
   begin
      Edit1.Text := 'content from OnActivate';
   end;

FORMB - OnActivate事件

procedure TFormB.FormActivate(Sender: TObject);
begin

     Edit1.Text := 'content from OnActivate';
end;

FORMC - OnActivate事件

procedure TFormC.FormActivate(Sender: TObject);
begin

        Edit1.Text := 'content from OnActivate';
end;

调用PR_CreateOpenForm(TFormC)时出错

Error when calls Action3

DEBUG - 逐步运行此事件处理程序错误:

procedure TWinControl.MainWndProc(var Message: TMessage);
begin
  try
    try
      WindowProc(Message);
    finally
      FreeDeviceContexts;
      FreeMemoryContexts;
    end;
  except
    Application.HandleException(Self);
  end;
end;

如果我必须提供任何其他信息/代码,请告知我们,以便获得您的建议和宝贵的提示!

谢谢你们!

3 个答案:

答案 0 :(得分:0)

尝试将正在运行的OpenDiretorioExecute方法更改为下面显示的代码,并告诉我您是否仍然收到错误。在PR_OpenForm中使用vform.OnActivate(Self)。还请显示TFormDiretorio的OnActivate事件处理程序。

procedure TFormHome.OpenDiretorioExecute(Sender: TObject);
begin
  FormDiretorio:=PR_OpenForm(TFormDiretorio) as TFormDiretorio;

(* if Not Assigned(FormDiretorio) then
     begin               
           FormDiretorio              := TFormDiretorio.Create(Self);
           FormDiretorio.Parent       := PanelCorpo;
           FormDiretorio.Align        := alclient;
           FormDiretorio.BorderIcons  := [biSystemMenu];
           FormDiretorio.BorderStyle  := bsNone;
           FormDiretorio.Show;
           FormDiretorio.SetFocus;
           FormDiretorio.OnActivate(Sender);  // In this way , OnActivate works fine
     end; *)
end;  

更改PR_OpenForm以返回vForm。我假设您需要变量FormDiretorio。

答案 1 :(得分:0)

错误出现在这段代码中:

if Not Assigned(TForm(PClassform)) then
begin
  vform := PClassform.Create(Application);
end;

如果您查看源代码以实现Assigned(),您将看到它只检查传入的参数是否为零。因此,您可能认为代码不会检查是否存在PClassForm类型的表单。它仅检查参数PClassForm是否为零。

在您的情况下Assigned()返回true,表单未创建,随后vform包含堆栈中发生的任何内容。它只会在您调用OnActivate()的行中崩溃,这只是一个巧合。您可能通过访问未初始化的vform变量来销毁重要数据(可能还有)。

为防止此类错误致命,您应将本地指针变量初始化为nil(如果它们可能保持未初始化状态)。你可能也有一个编译器警告,但忽略了它。

早些时候我想问你在哪里打算对你创建的表单进行引用,以便你可以访问它们,但我没有,因为这不是你的问题。

您需要对此进行决定,然后使用这些引用来检查存在并访问表单。

答案 2 :(得分:0)

<强>更新

您在评论中提问

  

关键是:给定TformClass类的参数PClassForm,如何检查在Application中是否创建了这样的参数实例? &#34; ,

您可以使用下面FormInstance之类的功能执行此操作。 VCL应用程序的Screens对象具有Forms属性,您可以迭代它,查看其中一个表单是否为指定类,并将其作为函数的结果返回(否则为Nil)。一旦找到了实例,你当然可以使用强制转换来调用它的一些特定方法。

function FormInstance(AClass : TClass) :  TForm;
var
  i : Integer;
begin
  Result := Nil;
  for i := 0 to Screen.FormCount - 1 do begin
    if Screen.Forms[i].ClassType = AClass then begin
      Result := Screen.Forms[i];
      Break;
    end;
  end;
end;

procedure TMyForm.Button1Click(Sender: TObject);
var
  F : TForm;
begin
  F := FormInstance(TForm2);
  if F <> Nil then
    Caption := 'Found'
  else
    Caption := 'Not found';
end;

原始回答您编写q的方式似乎纠结了您的实际技术问题

  

vform.OnActivate(??); //这是问题,如何调用此事件?

有很多与之无直接关系的问题。而是尝试 调用OnActivate处理程序(如果有),可能会更好 覆盖表单的Activate过程,以执行您想要的任何特殊处理 然后将其留给TForm中的代码,以决定何时调用OnActivate。这不太可能导致其他形式的行为错误(如TScreen)。

下面的代码显示了如何执行此操作。

type
  TForm1 = class(TForm)
    procedure FormActivate(Sender: TObject);
  protected
    procedure Activate; override;
  public
  end;

[...]
procedure TForm1.Activate;
begin
  inherited;
  Caption := Caption + ' called from TForm1.Activate';
end;

procedure TForm1.FormActivate(Sender: TObject);
begin
  Caption := 'Activated';
end;

当然,也许您可​​以将OnActivate中要执行的代码放在OnShow处理程序中。