使用ModalResult退出表单

15

我有一堆表单,我想自动化它们,让它们可以自己打开和关闭。

我知道如何让它们打开(通过设置OnActivate函数),但是我无法关闭它们。

例如,我有:

procedure TProgressForm.FormActivate(Sender: TObject);
begin
  inherited;
  if FModItem.IsInQueue then
    begin
      RunBtnClick(Self);
      ModalResult := mrOK;
    end;    
end;

我想在运行函数后关闭窗口,这就是ModalResult应该完成的功能。

(我还尝试将ModalResult语句添加到RunBtnClick过程的最后,但也没有起作用)

我是这样创建窗体的:

ProgForm := TProgressForm.Create(Self, FModItem);
Self.Visible := False;
try
 if ProgForm.ShowModal = mrOK then
  begin
    Left := ProgForm.Left;
    Top := ProgForm.Top;
  end;

我已经能够通过在对象检查器中将模态结果添加到mrOK来创建关闭表单的按钮,但我似乎无法明确地执行它。

有人能看出为什么它不起作用吗?

谢谢


我不明白。你是通过编写 OnActivate 事件处理程序来“打开 [一个表单]”的吗? - Andreas Rejbrand
@ Andreas,我创建了一个表单,并添加了OnActivate处理程序,这样创建的表单会运行一个过程,打开另一个表单。 - KingKong
4个回答

17

不起作用的原因是VCL在显示表单后但在开始检查ModalResult更改之前,会主动将ModalResult设置为0。因此,在OnActivate和OnShow中,你都来得太早了。

解决方案是延迟通知。可以通过发送PostMessage来实现,具体如下:

const
  UM_ACTIVATED = WM_USER + 1;

type
  TProgressForm = class(TForm)
    procedure FormActivate(Sender: TObject);
  private
    procedure UMActivated(var Message: TMessage); message UM_ACTIVATED;
  end;

...

procedure TProgressForm.FormActivate(Sender: TObject);
begin
  PostMessage(Handle, UM_ACTIVATED, 0, 0);
end;

procedure TProgressForm.UMActivated(var Message: TMessage);
begin
  { Your code here }
  ModalResult := mrOk;
end;

来源: NLDelphi


1
顺便说一下:SendMessage 不起作用,因为它不会返回结果,直到它得到一个结果。另一方面,PostMessage 会立即返回,并将消息放入队列以供将来处理。 - NGLN
他们还没有修复这个问题吗?真是让人恼火。我在C++ Builder中遇到了这个问题... - James Johnston

6
我会覆盖ShowModal并在那里执行你现在在OnActivate中进行的测试。这有两个重要的优点:
  • 如果不需要显示表单,则根本不会显示表单。从OnActivate启动表单关闭会导致表单在屏幕上“闪烁”:它被显示然后立即被关闭。
  • 不依赖于不受您控制的代码。您不再关心祖先ShowModal中的操作顺序,因为只有在实际需要显示表单时才调用它。

当然,以这种方式使用GUI元素(窗体)有点代码异味,因为它基本上使用了GUI而不需要用户交互。毫无疑问,可以重构为使用一个中介函数,该函数返回mrOk并执行RunBtnClick()所做的操作,而不需要GUI,并仅在需要时创建Form。我想这是一种成本效益的情况。

代码:

TMyForm = class(TForm)
....
public
  function ShowModal:Integer;override;
end;

function TMyForm.ShowModal:Integer;
begin
  if FModItem.IsInQueue then
    begin
      RunBtnClick(Self);
      Result := mrOK;
    end
  else
    Result := inherited ShowModal;
end;

3
< p > 在 TCustomForm.ShowModal 重置 ModalResult 为 mrNone 之前将触发 OnActivate 事件。这意味着在您的 OnActivate 处理程序中更改 ModalResult 将被忽略。 < /p >
  function TCustomForm.ShowModal: Integer;
  Show;
  try
    SendMessage(Handle, CM_ACTIVATE, 0, 0);  << Your onActivate is called here
    ModalResult := 0; << ModalResult is reset

2
请看 forms.pas 中的 TCustomForm.ShowModal 函数:在发送 CM_ACTIVATE 消息(该消息触发 OnActivate 调用)之后才会检查 ModalResult;事实上,在您的 OnActivate 调用返回后,它会立即设置为 0,因此您的赋值操作无法生效。我不想过多干涉这个问题(您的代码明显没有通过测试),但您可以尝试添加类似以下内容的代码:
if ModalResult=0 then 
     SendMessage(Handle, CM_ACTIVATE, 0, 0);

将其添加至您的事件处理程序的顶部。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接