delphi - 显示异步“等待”窗口
问题描述
在 Delphi VCL 应用程序中,我想创建一个“等待”消息窗口,因为这是一个耗时的过程(本例中的一个大循环)。
在开始耗时的过程之前,我尝试了以下要执行的操作。
- 创建一个包含消息的简单窗口的新形式。
- 使用 messagedlg 创建一条消息。
- 甚至更改主表单上的 TLabel.Caption(执行耗时过程的表单)。
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,
popUpMessage;
type
TForm1 = class(TForm)
Button1: TButton;
Label1: TLabel;
procedure Button1Click(Sender: TObject);
end;
var
Form1: TForm1;
dialog : TForm;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var i, j, k :LongInt;
begin
{1}
popUpMessage.Form2 := TForm2.Create(nil); //also tried with Create(self)
Form2.show;
{2}
dialog := CreateMessageDialog ('Wait',TMsgDlgType.mtWarning, mbYesNoCancel);
dialog.Show;
{3}
messagedlg('Wait',mtError, mbOKCancel, 0);
{4}
Label1.Caption := 'Wait';
//Time consuming process
for i := 0 to 200000 do
for j := 0 to 20000do
k := i-j;
end;
end.
在 {1} 和 {2} 的情况下,弹出表单出现在耗时的过程开始之前,但它们的组件仅在完成后才绘制。
情况 {3} 保持执行,直到模式对话框关闭。
如果{4}在耗时的过程完成后标题会发生变化。
如何异步创建消息,使其完全绘制而不等待其父进程?
解决方案
这是一个有点宽泛的问题,因此我将提供一个简短的示例来演示如何将长时间运行的任务移至线程并安全地向 UI 提供反馈以了解进度和完成情况。
我不会为第二个进度表单而烦恼,对于这个例子,我只是将一个按钮 (Button1) 和一个进度条 (ProgressBar1) 添加到一个新的 TForm 中。
在下面的方法中,我们启动一个匿名线程来执行长时间运行的操作,并以定期的进度间隔定期通知主线程。我们还会在工作完成时通知。为了扩展它,您还可以执行错误检查/处理,并且还可以通知操作未能完成,但为了简洁起见,我将示例限制为简单的东西。
procedure TForm1.Button1Click(Sender: TObject);
begin
ProgressBar1.Position := 0;
Button1.Enabled := false;
Button1.Caption := 'Calculating...';
TThread.CreateAnonymousThread(
procedure
procedure PerformProgressStep;
begin
TThread.Queue(nil, procedure
begin
ProgressBar1.StepIt;
end);
end;
procedure NotifyComplete;
begin
TThread.Queue(nil, procedure
begin
ShowMessage('done');
Button1.Enabled := true;
Button1.Caption := 'Start Task';
end);
end;
var
i, j, k : integer;
begin
for i := 0 to 199999 do begin
for j := 0 to 20000 do k := i-j;
if (i mod 20000) = 0 then PerformProgressStep;
end;
NotifyComplete;
end).Start;
end;
这里所有的 UI 操作都使用TThread.Queue
. .Synchronize
如果您必须等待主线程处理工作才能继续线程中的操作,这也是一种选择,但您必须注意避免在这种情况下出现死锁情况。
此代码中没有任何错误处理,请注意 - 这只是为了演示如何将您的工作转移到后台线程中。还有许多其他方法可以做到这一点,使用匿名线程(如上),如果您有更重的实现要封装,则使用自定义 TThread,使用任务和 RTL 线程池,使用Threading
单元中的并行循环操作等。无论哪种您决定使用将取决于您的特定应用程序的要求。
对于 Delphi 中的多线程更深入的探索,您可以随时跟进Martin Harvey 的优秀文章。这是一篇相当老的文章,因此它在较低级别上涉及多线程主题,并使用基本的 RTL 和 WinAPI 结构,并且不包含更现代版本的 Delphi 中包含的任何新语言功能。然而,它具有很强的指导性,是复习基础知识的绝佳读物。
推荐阅读
- python - 是否可以将 reportlab.pdfgen.canvas.Canvas 转换为 SVG?
- android - R8 和 Proguard 规则
- c# - WPF TreeView中的多级多选
- php - Php/MySql 查找/替换 json 中的 utf-8 字符串
- html - jQuery:如何在单击按钮上将其传递给我的函数?
- html - Float 将所有内容移到一侧,HTML 和 CSS
- java - Hashmap - 迭代并打印太多次输出
- performance - 谷歌表格脚本超过最大执行时间
- arrays - 如何根据 Angular Reactive Forms 重新分配嵌套数组键?
- java - 无法使用 power mock 模拟最终课程