【问题标题】:How to close non-modal form in Delphi如何在 Delphi 中关闭非模态表单
【发布时间】:2010-01-15 23:20:34
【问题描述】:

这里已经讨论过了,但没有详细讨论。

我在尝试关闭非模态子表单时遇到了问题。我让它通知父母,但我收到抽象错误和其他异常。我究竟做错了什么?父母是否必须释放非模态表单,或者不再尝试通过该变量访问它?

主要形式:

NonModal := NonModalTForm.Create(Self);
NonModal.Callback := Callback;
NonModal.Show;

Procedure TForm.Callback; // called by non-modal form when closing 
begin
   FreeAndNil(NonModal);  // or should this just be NonModal := nil so I don't try to access a dangling pointer?
end;

在 NonModal.pas 中

procedure NonModalTForm.FormClose;
begin
  Callback; // calls parent
end;

【问题讨论】:

  • 您的代码相当于切断您所在的分支的编程。

标签: delphi


【解决方案1】:

您调用close 从FormClose 事件以外的某个地方关闭您的表单。在 FormClose 事件中,只需将 Action 设置为以下之一:

  • caFree - 完全处理表单
  • caMinimize - 最小化表单
  • caHide - 隐藏表单
  • caNone - 忽略关闭

例如:

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end;

【讨论】:

  • 虽然我多年来一直是这种方法的好朋友,但我发现这在使用 VCL 样式时会生成随机的EAccessViolations,例如在System.@UStrAddRefSystem.@UStrLAsg
  • 刚刚了解到caFree 的这些访问冲突仅发生在模态表单中。非模态形式+VCL风格都可以。
  • @Anse 为什么要使用 caFree 作为模态表单?一个模态表单是阻塞的,所以在你的表单显示模态之后,你将它从调用者中释放出来。
【解决方案2】:

VCL 已经有一种机制可以在其他组件被释放时通知组件。你可以这样使用它;

type
  TfrmParent = class(TForm)
    btnShowChild: TButton;
    procedure btnShowChildClick(Sender: TObject);
  private
    FChild: TfrmChild;
  public
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
  end;


procedure TfrmParent.btnShowChildClick(Sender: TObject);
begin
  // Check status of child
  if FChild = nil then
  begin
    // Child does not exist, create it
    FChild:= TfrmChild.Create(Application);
    FChild.Show;

    // Ask Child to notify us when it is destroyed
    FChild.FreeNotification(Self);
  end
  else
  begin
    // Child already exists
    FChild.Show;
  end;
end;

procedure TfrmParent.Notification(AComponent: TComponent;
  Operation: TOperation);
begin
  inherited;

  if (AComponent = FChild) and (Operation = opRemove) then
  begin
    // FChild is about to be freed, so set reference to Child to nil
    FChild:= nil;
  end;
end;

创建子表单后,使用创建的表单的 FreeNotification 方法注册自己,以便在子表单被销毁时收到通知。

要对通知做出反应,请覆盖 Notification 方法。在那里,您可以找出哪个组件被销毁,并将其与记住的对子表单的引用进行比较。当您收到通知时,只需将子表单的引用设置为 nil。

在子 TfrmChild 本身中,除了 skamradt 所写的内容外,您无需执行任何其他操作:只需在 OnClose 事件中将参数 Actionb 设置为 caFree。

【讨论】:

  • +1。当 Self 被传递给 TfrmChild.Create() 而不是 Application 时,甚至不需要调用 FreeNotification() - VCL 中的所有权管理负责通知调用。
  • 这里唯一可行的决定。只有它将封闭形式设置为nil。不错的作品。我想知道为什么选票更少
【解决方案3】:

如果您想稍后显示窗口,请使用隐藏。

如果要关闭它,请使用 Close。 (关闭主窗口,关闭应用程序)。 Close 的具体操作取决于表单参数。

查看关闭的来源:

procedure TCustomForm.Close;
var
  CloseAction: TCloseAction;
begin
  if fsModal in FFormState then
    ModalResult := mrCancel
  else
    if CloseQuery then begin
      if FormStyle = fsMDIChild then
        if biMinimize in BorderIcons then
          CloseAction := caMinimize 
        else
          CloseAction := caNone
      else
        CloseAction := caHide;
      DoClose(CloseAction);
      if CloseAction <> caNone then
        if Application.MainForm = Self then 
          Application.Terminate
        else if CloseAction = caHide then 
          Hide
        else if CloseAction = caMinimize then 
          WindowState := wsMinimized
        else 
          Release;
    end;
end;

但要小心免费。 Windows 队列中可能有一些消息可能导致崩溃。最好使用 Release 来清理窗口。因为这会在释放它之前等待消息。

【讨论】:

    【解决方案4】:

    你正在做你不应该做的事。
    在 NonModalForm 的 onClose 事件中,您调用一些直接释放它的代码,而它仍在事件处理程序执行中,因此您最终得到一个不再有效的 self 对象。
    这就是为什么在表单上使用Release 而不是Free 的海报案例。

    正如 Gamecat 指出的那样,只需调用 Close...
    VCL 的美妙之处往往在于它就这么简单。

    【讨论】:

    • >>正如 Gamecat 指出的那样,只需简单地调用 Close...
    • 您的Callback 函数与本次讨论无关,Tom。正如您所注意到的,您在表单的 OnClose 事件处理程序中调用它。这意味着当回调函数被调用时,你的表单已经关闭,所以你的整个问题没有实际意义:你不需要学习如何关闭表格,因为你已经完成了,即使你不知道你是如何管理它的。
    • 如果没有回调过程,如何避免父指针指向非模态表单的内存泄漏和/或悬空指针?我必须释放 NonModal 指向的内存......并将 NonModal 设置为 nil ,所以我知道它没有被使用。我能看到的唯一解决方案是另一层逻辑:非模态表单在关闭时调用父级回调,它将标志“非模态表单关闭”设置为 True。稍后(当它本身关闭或想要重用非模态表单时,它会检查此标志并释放内存。抱歉在此徘徊,但我真的想在没有的情况下解决这个问题
    • 您不需要 Free NonModal,因为它是通过将 Self(MainForm)作为 Owner 传递来创建的。主人会在它自己毁灭时释放它。在此之前,除非它被某些代码明确地释放和取消,否则它仍然可以在需要时被重用。如果你需要自己释放它,你可以随时检查它是否已经关闭。
    • 谢谢弗朗索瓦和其他人。我的问题现在得到了解答。我的困惑源于我不明白 NonModal.Close 留下可供重用的事实。再次感谢大家的帮助!
    【解决方案5】:

    不要使用callback

    只需调用FreeAndNil(Self); 即可释放为表单创建的所有内存资源。

    请记住释放由您的实现代码创建的对象。 Delphi 对表单设计器创建的对象进行了很好的清理。

    【讨论】:

    • 我认为这个答案不能补充已经给出的其他答案。其次,正如this answer(尤其是this comment on it)所说,在其事件处理程序中释放表单确实是不行的。抱歉,-1。
    猜你喜欢
    • 1970-01-01
    • 2013-03-27
    • 2023-03-13
    • 1970-01-01
    • 2019-06-25
    • 2013-09-28
    • 1970-01-01
    • 2012-01-23
    • 1970-01-01
    相关资源
    最近更新 更多