【问题标题】:Delphi disable form while loadingDelphi 在加载时禁用表单
【发布时间】:2013-07-04 11:44:31
【问题描述】:

在我的应用程序中,我有一个主窗体,能够在数据库中加载一些图像。 在加载图像时,我想显示一个带有进度指示器的表单(带有 bsNone 边框样式)。

但是,如果我使用 ShowModal 显示表单,主表单的执行将停止,所以我不能这样做。

如果我调用 Show,用户可以访问所有其他表单组件,这可能很危险,而照片未完全加载。

我需要找到方法,在加载未完成时禁用主窗体上的所有内容。

请告诉我,这怎么可能。

【问题讨论】:

  • 如果您想保留一个模态表单,您可以从您的模态显示表单调用例程。例如通过移交在模态显示表单的构造函数中调用的方法。
  • 除了禁用表单外,您还可以隐藏主表单。其他可能性包括从模态进度对话框调用加载过程。

标签: delphi tform


【解决方案1】:

设置子窗体的PopupParent = ParentForm

procedure TParentForm.Button1Click(Sender: TObject);
begin
ParentForm.Enabled:=False;
with Tform1.create(nil) do show;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
ParentForm.Enabled := true;
form1.free;
end;

【讨论】:

    【解决方案2】:

    您可能想要使用DisableTaskWindowsEnableTaskWindows 函数。

    如果您对父表单使用简单的ParentForm.Enabled := False,您仍然可以访问所有其他表单,例如与ParentForm 不同的主表单。显然还是很危险的。

    这是一个简短的示例:

    interface
    
    uses
      Vcl.Forms, Winapi.Windows, ...;
    
    type
      TPleaseWait = class(TObject)
      private
        fWindowList: TTaskWindowList;
        fActiveWindow: HWND;
        fDialog: TPleaseWaitForm;
      public
        constructor Create;
        destructor Destroy; override;
      end;
    
    implementation
    
    constructor TPleaseWait.Create;
    begin
      // Disable all displayed windows
      fWindowList := DisableTaskWindows(0);
    
      // Save the last active window
      fActiveWindow := GetActiveWindow;
    
      fDialog := TPleaseWaitForm.Create(nil);
      fDialog.Show;
    end;
    
    destructor TPleaseWait.Destroy;
    const
      INVALID_HANDLE = 0;
    begin
      fDialog.Close;
      fDialog.Free;
    
      // All windows are enabled now
      EnableTaskWindows(fWindowList);
    
      // That helps by enabling the last one form
      if (fActiveWindow <> INVALID_HANDLE) and IsWindow(fActiveWindow) then
        SetActiveWindow(fActiveWindow);
      inherited;
    end;
    
    end.
    

    【讨论】:

      【解决方案3】:

      正如我在其他答案中所解释的,将长时间运行的任务放入 GUI 线程以外的线程是理想的解决方案。 GUI 线程应该处理短时间运行的任务,以便它始终能够及时地为消息队列提供服务。

      但是,如果您已经有代码假定长时间运行的任务在 GUI 线程上运行,您可能更愿意采用更方便的方法并将重构推迟到线程代码。在这种情况下,在我看来,使用ShowModal 来显示您的进度表会更好。

      但为了使这项工作正常进行,您需要在 ShowModal 调用中找到一个让长时间运行的任务执行。你可以这样做:

      1. 在调用ShowModal 之前,将任务传递给表单。例如,将TProc 传递给进度表单的构造函数。
      2. 覆盖进度表的Activate 方法。这将在ShowModal 函数开始其模式消息循环之前执行。在Activate的实现中,向表单发布消息。
      3. 当表单处理该消息时,调用传递给构造函数的任务。

      显然,您需要在长时间运行的任务中调用 ProcessMessages,以保持主 GUI 线程消息队列得到服务。显然你一定已经在这样做了。

      【讨论】:

        【解决方案4】:

        首先,直接回答你的问题。

        我需要设法禁用主窗体上的所有内容。

        MainForm.Enabled 设置为False 以禁用与主窗体关联的窗口。并重新启用将其设置为 True


        然而,您的根本问题是您在 GUI 线程中执行长时间运行的任务。这总是一个坏主意,出路是在单独的线程中执行那些长时间运行的任务。

        一旦您将长时间运行的任务移至单独的线程,您会发现ShowModal 正是您显示进度表单所需要的。

        【讨论】:

          【解决方案5】:

          MainForm 设置为进度表单的PopupParent,以便MainForm 永远不会出现在进度表单的顶部。然后在进度表打开时设置MainForm.Enabled := False,在进度表关闭时设置MainForm.Enabled := True

          procedure TMainForm.ShowProgressForm;
          begin
            with TProgressForm.Create(nil) do
            begin
              PopupParent := Self;
              OnClose := ProgressFormClose;
              Show;
            end;
            Enabled := False;
          end;
          
          procedure TMainForm.ProgressFormClose(Sender: TObject; var Action: TCloseAction);
          begin
            Action := caFree;
            Enabled := True;
          end;
          

          这模拟了ShowModal() 对用户的行为方式(MainForm 在进度表单打开时不是用户交互的),但不会阻塞代码。

          【讨论】:

            猜你喜欢
            • 2016-02-16
            • 2014-01-23
            • 2021-06-28
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-08-17
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多