首页 > 解决方案 > 如何从 TThread 显示“请稍候”模式表单并在工作完成后释放它?

问题描述

我已经工作了一段时间,试图制作一个模态表单来通知用户等到工作结束。这是我正在尝试做的一个简单示例:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
  TLoader = class(TThread)
    private
      FStrings: TStrings;
      procedure ShowWait;
      procedure EndsWait;
    public
      Constructor Create(AStrings: TStrings);
      Destructor Destroy; override;
      procedure Execute; override;
  end;
var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
Var
  List: TStrings;
begin
  List := TStringList.Create;
  try
    // Load Some Data here
    TLoader.Create(List);
  finally
    List.Free;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin

end;

{ TLoader }

constructor TLoader.Create(AStrings: TStrings);
begin
  inherited Create;
  FreeOnTerminate:= True;
  FStrings:= TStringList.Create;
  FStrings.AddStrings(AStrings);
end;

destructor TLoader.Destroy;
begin
  FStrings.Free;
  inherited;
end;

procedure TLoader.EndsWait;
begin
  TForm(Application.FindComponent('FWait')).Free;
end;

procedure TLoader.Execute;
begin
  inherited;
  Synchronize(ShowWait);
  // Do Some Job while not terminated
  Sleep(1000);
  // Free Wait Form
  // This part is not working
  Synchronize(EndsWait);
end;

procedure TLoader.ShowWait;
begin
  With TForm.Create(Application) do
    begin
      // Some code
      Name:= 'FWait';
      ShowModal;
    end;
end;

end.

一切都按我的预期工作,除了Synchronize(EndsWait);没有关闭和释放模态表单。

如何在运行时显示模态表单并在终止TThread时释放它?TThread


更新:

我尝试按照 Remy 的建议执行以下操作:

type
  TForm2 = class(TForm)
    procedure FormShow(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;
  TLoader = class(TThread)
    protected
      procedure DoTerminate; override;
      procedure DoCloseModal;
    public
      constructor Create;
      procedure Execute; override;
  end;
var
  Form2: TForm2;

implementation

{$R *.dfm}

{ TLoader }

constructor TLoader.Create;
begin
  inherited Create;
  FreeOnTerminate:= True;
end;

procedure TLoader.DoCloseModal;
begin
  Form2.ModalResult:= mrOk;
end;

procedure TLoader.DoTerminate;
begin
  inherited DoTerminate;
  Synchronize(DoCloseModal);
end;

procedure TLoader.Execute;
begin
  inherited;
  Sleep(200);
end;

procedure TForm2.FormShow(Sender: TObject);
begin
  TLoader.Create;
end;

end.

主窗体按钮单击事件处理程序:

procedure TForm1.Button1Click(Sender: TObject);
begin
  with TForm2.Create(nil) do
    try
      ShowModal;
    finally
      Free;
    end;
end;

标签: multithreadingdelphimodal-dialogdelphi-10-seattle

解决方案


你有两个选择:

  1. 不要一开始就使用模态形式。TThread.Synchronize()阻塞你的线程直到同步的方法退出,但TForm.ShowModal()阻塞该方法直到窗体关闭。使用TThread.Synchronize()(或更好,TThread.Queue()Create()+ Show()(不是ShowModal())等待表单,然后返回线程并让它根据需要完成其工作,然后Synchronize()/Queue()再次(或使用线程的OnTerminate事件)在完成后Close()+Free()等待表单。

  2. 或者,如果您想使用模式等待表单,则根本不要让线程管理等待表单。让您的按钮OnClick处理程序Create()+ ShowModal()(不是Show())等待表单,并Free()ShowModal()退出时使用它。分别在显示和关闭等待表单时让等待表单在内部创建和终止线程。如果线程在等待窗体关闭之前结束,线程的OnTerminate处理程序可以Close()在窗体(它只是设置窗体ModalResult)中ShowModal()退出,以便在OnClick处理程序中退出。


推荐阅读