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

时间:2016-02-21 22:49:31

标签: delphi vcl jvcl

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

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

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

enter image description here

任何想法?

2 个答案:

答案 0 :(得分: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.

<强> 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

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.

<强> 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

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

  1. 为了避免从线程访问VCL数据 - 如果必须在例程中重用已修改的数据,那么它是首选方式:
    • 将网格数据的副本传递给线程 - 比如构造函数
    • 更新副本
    • 在线程完成后,即在ShowModal返回后,使用编辑后的数据副本更新网格。
  2. 要从线程访问表单的对象 - ,如果以非常短的时间间隔访问表单对象,则可以完成此操作
    • 使用synchronized block从网格中获取数据
    • 在线程的同步回调中​​更新网格 - 即myRunProgressmyRunTerminate方法
  3. 对于不同的用例,混合方法也可以有意义(在构造函数中传递副本/在线程的同步块中更新网格)如果你的例程没有考虑已经更改的数据:选择最适合您需求的方法。

    如果另一个外部线程更新了网格,thread1可以读取数据,然后填写表单的私有队列 - 比如TThreadListTCriticalSection块中的其他集合 - 并通知thread2在队列中执行作业,但我希望可能不需要这样做。

答案 1 :(得分:0)

创建对话框表单设置:

BorderIcons = []
BorderStyle = bsDialog
FormStyle = fsStayOnTop
Position = poScreenCenter
当您调用函数时,在主窗体中

procedure TFormMain.Button1Click(Sender: TObject);
begin
    Enabled:=false;
    try
        FormDialog.Show;
        FormDialog.Refresh;

        MyLongRunProcedure; // calls your procedure here

    finally        
        Enabled:=true;
        FormDialog.Close;
    end;
end;

它应该有用..