显示异步“等待”窗口

Mik*_*mik 1 delphi asynchronous non-modal

在 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.
Run Code Online (Sandbox Code Playgroud)

在 {1} 和 {2} 的情况下,弹出窗体出现在耗时的过程开始之前,但它们的组件仅在此过程完成后才绘制。

Case {3} 保持执行直到模式对话框关闭。

在 {4} 的情况下,在耗时的过程完成后标题发生变化。

如何异步创建消息,以便在不等待其父进程的情况下完全绘制?

J..*_*... 5

这是一个有点宽泛的问题,因此我将提供一个简短的示例来演示如何将长时间运行的任务移至线程并安全地向 UI 提供反馈以进行进度和完成。

我不会打扰第二个进度表,在本例中,我只是在新的 TForm 中添加了一个按钮 (Button1) 和一个进度条 (ProgressBar1)。

在下面的方法中,我们启动一个匿名线程来执行长时间运行的操作,并以固定的进度间隔定期通知主线程。我们也会在工作完成时通知。为了扩展这一点,您还可以执行错误检查/处理,还可以通知操作无法完成,但为了简洁起见,我将示例限制为简单的事情。

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;
Run Code Online (Sandbox Code Playgroud)

这里所有 UI 操作都使用TThread.Queue. .Synchronize如果在继续线程中的操作之前必须等待主线程处理工作,这也是一种选择,但在这种情况下必须注意避免死锁情况。

这段代码中没有任何错误处理,也请注意 - 这只是为了演示如何将您的工作移到后台线程中。还有很多其他方法可以做到这一点,使用匿名线程(如上所述),如果您有更重的实现要封装,则使用自定义 TThread,使用任务和 RTL 线程池,使用Threading单元中的并行循环操作等。无论哪种方式您决定使用将取决于您的特定应用程序的要求。


要更深入地探索 Delphi 中的多线程,您可以随时在这里跟进Martin Harvey 的优秀文章。这是一篇很老的文章,因此它在较低级别上涉及多线程主题,并使用基本的 RTL 和 WinAPI 构造,并且不包括更现代的 Delphi 版本中包含的任何更新的语言功能。然而,它非常有启发性,是复习基础知识的绝佳读物。