在背景中运行功能时,将表单作为叠加层加载

Hid*_*den 2 delphi vcl jvcl

当我调用一个函数并且它"运行"时(可能需要3秒钟 - 刷新函数从api服务器获取数据)我想将一个加载形式显示为Ajax加载指示器作为主窗体上方的叠加层.

我以前的尝试都失败了.我曾尝试更改在创建Main之后直接显示的Create the LoadingForm.然后我尝试了LoadingForm.Show/Showmodal.在模态序列中停止并且仅在我关闭表单时继续并且显示尽管窗口不关闭.

我也有这样的情况,表格打开但gif没有显示,应该是的地方只是白色并保持白色 - 没有图像没有动画

在此输入图像描述

任何想法?

fan*_*cco 9

下面的代码使用一个线程模仿其Execute方法中的长时间运行块,并使用OnProgress"回调"来通知表单已完成的百分比已更改.

这是一个非常小的例子,但它可以告诉你我认为正确的方向之一.
请注意,当前未执行错误检查或异常处理.


Unit1.pas 主窗体和线程类

unit Unit1;

interface

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

type
  TMyRun = class(TThread)
    protected
      procedure Execute; override;
    public
      OnProgress: TProgressEvent;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    FProgressForm: TfrmProgress;
    procedure myRunProgress(Sender: TObject; Stage: TProgressStage;
        PercentDone: Byte; RedrawNow: Boolean; const R: TRect; const Msg: string);
    procedure myRunTerminate(Sender: TObject);
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TMyRun.Execute;
var
  i: Integer;
  r: TRect;
begin
  for i := 1 to 100 do begin
    if Terminated then
      Break;

    Sleep(50);//simulates some kind of operation

    if Assigned(OnProgress) then
      Synchronize(procedure
          begin
            OnProgress(Self, psRunning, i, False, r, '');
          end);
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FProgressForm := TfrmProgress.Create(nil);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FProgressForm.Free;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  with TMyRun.Create do begin
    FreeOnTerminate := True;
    OnProgress := myRunProgress;
    OnTerminate := myRunTerminate;
  end;
  FProgressForm.ShowModal;
end;

procedure TForm1.myRunProgress(Sender: TObject; Stage: TProgressStage;
  PercentDone: Byte; RedrawNow: Boolean; const R: TRect; const Msg: string);
begin
  FProgressForm.ProgressBar1.Position := PercentDone;
end;

procedure TForm1.myRunTerminate(Sender: TObject);
begin
  FProgressForm.Close;
end;

end.
Run Code Online (Sandbox Code Playgroud)

Unit1.dfm

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 81
  ClientWidth = 181
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  Position = poDesktopCenter
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  PixelsPerInch = 96
  TextHeight = 13
  object Button1: TButton
    Left = 48
    Top = 24
    Width = 91
    Height = 25
    Caption = 'Run the thread'
    TabOrder = 0
    OnClick = Button1Click
  end
end
Run Code Online (Sandbox Code Playgroud)

Unit2.pas 进度对话框

unit Unit2;

interface

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

type
  TfrmProgress = class(TForm)
    ProgressBar1: TProgressBar;
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  frmProgress: TfrmProgress;

implementation

{$R *.dfm}

end.
Run Code Online (Sandbox Code Playgroud)

Unit2.dfm

object frmProgress: TfrmProgress
  Left = 0
  Top = 0
  BorderStyle = bsSizeToolWin
  Caption = 'frmProgress'
  ClientHeight = 51
  ClientWidth = 294
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object ProgressBar1: TProgressBar
    Left = 16
    Top = 16
    Width = 265
    Height = 17
    TabOrder = 0
  end
end
Run Code Online (Sandbox Code Playgroud)

请参阅注释,该注释指出长时间运行的操作需要访问主窗体中的网格,以避免阻止该对象上的VCL线程:

  1. 为了避免从线程访问VCL数据 - 如果已经修改过的数据必须在例程中重用,那么这是首选的方法:
    • 将网格数据的副本传递给线程 - 比如在构造函数中
    • 更新副本
    • 在线程完成后 - 即ShowModal返回后,使用编辑的数据副本更新网格.
  2. 要从线程访问表单的对象 - 如果以非常短的时间间隔访问表单的对象,则可以执行此操作:
    • 使用synchronized块从网格中获取数据
    • 在线程的同步回调中​​更新网格 - 即在方法中myRunProgress或在myRunTerminate方法中

对于不同的用例,如果您的例程没有考虑已经更改的数据,那么混合方法也可能有意义(在构造函数中传递副本/在线程的同步块中更新网格):选择最适合您需求的方法.

如果另一个外部线程更新网格,a thread1可以读取数据,然后填写表单的私有队列 - 比如块中的一个TThreadList或另一个集合TCriticalSection- 并通知a thread2在队列上执行作业但我希望可能不需要这样做任务完成.

  • 我认为这个问题根本无法回答.增加赏金并不会让它变得更加清晰. (2认同)