如何从 Thread 显示 "Please wait " 模态表单并在工作完成后释放它?
How to show a "Please wait " modal form from a TThread and free it when the job is done?
我已经工作了一段时间,试图制作一个模态表单来通知用户等待作业结束。
这是我正在尝试做的一个简单示例:
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;
您有两个选择:
一开始不要使用模态形式。 TThread.Synchronize()
阻塞您的线程,直到同步方法退出,但 TForm.ShowModal()
阻塞该方法,直到表单关闭。使用 TThread.Synchronize()
(或更好,TThread.Queue()
)到 Create()
+Show()
(不是 ShowModal()
)等待表单,然后 return 到线程和让它根据需要完成它的工作,然后 Synchronize()
/Queue()
再次(或者,使用线程的 OnTerminate
事件)到 Close()
+Free()
Wait Form when完成。
或者,如果您想使用模态 Wait Form,则根本不要让线程管理 Wait Form。让你的按钮 OnClick
处理程序 Create()
+ShowModal()
(不是 Show()
)等待表单,并在 ShowModal()
退出时 Free()
它。当等待表单显示和关闭时,分别让等待表单在内部创建和终止线程。如果线程在 Wait Form 关闭之前结束,线程的 OnTerminate
处理程序可以 Close()
Form(它只是设置 Form 的 ModalResult
)以便 ShowModal()
将在OnClick
处理程序。
过了一段时间,我做了一个uWaitForm.pas
单元:
unit uWaitForm;
interface
uses
System.Classes, Vcl.Controls, Vcl.Forms;
type
TWaitForm = class(TForm)
private
FThread: TThread;
protected
procedure Activate; override;
procedure DoClose(var Action: TCloseAction); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
TWaitThread = class(TThread)
private
FForm: TWaitForm;
FModalResult: TModalResult;
protected
procedure Execute; override;
procedure DoSetModalResult;
public
constructor Create(AForm: TWaitForm);
destructor Destroy; override;
end;
implementation
{ TWaitForm }
procedure TWaitForm.Activate;
begin
inherited;
FThread:= TWaitThread.Create(Self);
end;
constructor TWaitForm.Create(AOwner: TComponent);
begin
inherited CreateNew(AOwner);
Name:= 'WaitForm';
BorderStyle:= bsDialog;
Caption:= 'Please wait...';
Width:= 200;
Height:= 150;
Position:= poDesktopCenter;
end;
destructor TWaitForm.Destroy;
begin
if Assigned(FThread) then
FThread.Terminate;
inherited;
end;
procedure TWaitForm.DoClose(var Action: TCloseAction);
begin
inherited;
if Assigned(FThread) then
begin
TWaitThread(FThread).FModalResult:= mrCancel;
FThread.Terminate;
end;
end;
{ TWaitThread }
constructor TWaitThread.Create(AForm: TWaitForm);
begin
inherited Create;
FreeOnTerminate:= True;
FForm:= AForm;
FModalResult:= mrOk;
end;
destructor TWaitThread.Destroy;
begin
if Assigned(FForm) then
Synchronize(nil, DoSetModalResult);
inherited;
end;
procedure TWaitThread.DoSetModalResult;
begin
FForm.ModalResult:= FModalResult;
end;
procedure TWaitThread.Execute;
begin
inherited;
// Do the work while not terminated
// You can check for Terminated here and set FModalResult accordingly to receive it from ShowModal
// By default it's mrOk (Job is done, the thread doesn't cancled)
Sleep(200);
end;
end.
那就用吧
with TWaitForm.Create(nil) do
try
//Get ModalResult here and do something accordingly
// mrCancel means the dialog form closed and the the Thread job aborted
// mrOk The job is done and the form closed
ShowModal;
finally
Free;
end;
我已经工作了一段时间,试图制作一个模态表单来通知用户等待作业结束。 这是我正在尝试做的一个简单示例:
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;
您有两个选择:
一开始不要使用模态形式。
TThread.Synchronize()
阻塞您的线程,直到同步方法退出,但TForm.ShowModal()
阻塞该方法,直到表单关闭。使用TThread.Synchronize()
(或更好,TThread.Queue()
)到Create()
+Show()
(不是ShowModal()
)等待表单,然后 return 到线程和让它根据需要完成它的工作,然后Synchronize()
/Queue()
再次(或者,使用线程的OnTerminate
事件)到Close()
+Free()
Wait Form when完成。或者,如果您想使用模态 Wait Form,则根本不要让线程管理 Wait Form。让你的按钮
OnClick
处理程序Create()
+ShowModal()
(不是Show()
)等待表单,并在ShowModal()
退出时Free()
它。当等待表单显示和关闭时,分别让等待表单在内部创建和终止线程。如果线程在 Wait Form 关闭之前结束,线程的OnTerminate
处理程序可以Close()
Form(它只是设置 Form 的ModalResult
)以便ShowModal()
将在OnClick
处理程序。
过了一段时间,我做了一个uWaitForm.pas
单元:
unit uWaitForm;
interface
uses
System.Classes, Vcl.Controls, Vcl.Forms;
type
TWaitForm = class(TForm)
private
FThread: TThread;
protected
procedure Activate; override;
procedure DoClose(var Action: TCloseAction); override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
end;
TWaitThread = class(TThread)
private
FForm: TWaitForm;
FModalResult: TModalResult;
protected
procedure Execute; override;
procedure DoSetModalResult;
public
constructor Create(AForm: TWaitForm);
destructor Destroy; override;
end;
implementation
{ TWaitForm }
procedure TWaitForm.Activate;
begin
inherited;
FThread:= TWaitThread.Create(Self);
end;
constructor TWaitForm.Create(AOwner: TComponent);
begin
inherited CreateNew(AOwner);
Name:= 'WaitForm';
BorderStyle:= bsDialog;
Caption:= 'Please wait...';
Width:= 200;
Height:= 150;
Position:= poDesktopCenter;
end;
destructor TWaitForm.Destroy;
begin
if Assigned(FThread) then
FThread.Terminate;
inherited;
end;
procedure TWaitForm.DoClose(var Action: TCloseAction);
begin
inherited;
if Assigned(FThread) then
begin
TWaitThread(FThread).FModalResult:= mrCancel;
FThread.Terminate;
end;
end;
{ TWaitThread }
constructor TWaitThread.Create(AForm: TWaitForm);
begin
inherited Create;
FreeOnTerminate:= True;
FForm:= AForm;
FModalResult:= mrOk;
end;
destructor TWaitThread.Destroy;
begin
if Assigned(FForm) then
Synchronize(nil, DoSetModalResult);
inherited;
end;
procedure TWaitThread.DoSetModalResult;
begin
FForm.ModalResult:= FModalResult;
end;
procedure TWaitThread.Execute;
begin
inherited;
// Do the work while not terminated
// You can check for Terminated here and set FModalResult accordingly to receive it from ShowModal
// By default it's mrOk (Job is done, the thread doesn't cancled)
Sleep(200);
end;
end.
那就用吧
with TWaitForm.Create(nil) do
try
//Get ModalResult here and do something accordingly
// mrCancel means the dialog form closed and the the Thread job aborted
// mrOk The job is done and the form closed
ShowModal;
finally
Free;
end;