【问题标题】:How to repaint a parent form while a modal form is active?如何在模态表单处于活动状态时重新绘制父表单?
【发布时间】:2010-09-22 06:35:54
【问题描述】:

在 Delphi 2006 中,我展示了一个模态表单。该表单中的用户输入可以更改当前可能显示在作为主表单的父表单上的数据。为了反映这些变化,我需要在主窗体上强制重新绘制一些自绘组件。我试图从模态表单中这样做:

MainForm := Application.MainForm;
MainForm.Invalidate;
MainForm.Update;

这并没有改变。我一直认为表单上的“更新”总是会立即重新绘制 - 显然不是这样。绘画代码本身应该没问题,因为我可以将模态表单移动到那些 ownerdraw 组件上以强制手动重绘。

但是当数据发生变化时,如何以编程方式强制重绘?

编辑:我将在下周尝试 Application.ProcessMessages 和 Refresh,感谢您的建议。

很抱歉这么久才回复,感谢所有回复的人。调用 Refresh() 是解决方案的一部分,但它必须在自定义绘图组件上完成,而不是在它们所在的表单上......现在我想接受多个答案;-)

【问题讨论】:

    标签: delphi


    【解决方案1】:

    也许值得一提的是,如果您使用的皮肤库也会影响事物。我发现我必须刷新皮肤库,而不是表单。

    【讨论】:

    • 我没有使用皮肤库,但自定义绘图组件可能与此相当。我必须“手动”找到这些组件并显式刷新它们。
    【解决方案2】:

    更新发送 WM_PAINT 消息。刷新通过执行绘制消息来强制重新绘制控件。请尝试使用 .Refresh。

    【讨论】:

    • 抱歉这么晚才回复。不幸的是 .Refresh 也没有工作。没有效果。数据仍然发生变化,如果我将模态对话框移到前面,我可以强制重新绘制。
    【解决方案3】:

    模态表单上的编辑是否直接写入父表单上的控件?如果是这样,那么它们应该自动更新。

    当您将模态表单移到父表单上时,它是否“变白”(即停止一起绘制)?如果是这样,那么您调用模态表单的方式或Kluge 建议的方式存在其他问题,您正在阻止发送消息(甚至可能是线程问题。)

    我对此进行了测试,默认情况下它可以工作。您需要两种形式,一种带有编辑框,另一种带有按钮。然后分配这些事件处理程序:

    procedure TForm1.Button1Click(Sender: TObject);
    begin
      Form2.ShowModal;
    end;
    

    procedure TForm2.Edit1Change(Sender: TObject);
    begin
      Form1.Button1.Caption := Edit1.Text;
    end;
    

    当您在第二个表单上更改 Edit1 时,它会更改 Form1 上按钮的标题。

    【讨论】:

    • “当您将模态表单移到父表单上时,父表单是否“变白”(即停止全部绘制)?”:不,它不会,而是绘制更改的数据 - 正如我在问题中写道。绘画作品,就是不知道怎么触发。
    【解决方案4】:

    试试下面的代码。只需将此 sn-p 插入您自己的代码中,然后在任何时间从任何地方调用它(但可能不是从线程,如果不同步可能会变得混乱)。它绘制应用程序中的所有窗口,而不依赖于消息循环。

    procedure UpdateApplication;
    // Updates (repaints where nesc) all windows of the app
      function UpdateWindow(hWnd: HWND; LParam: longint): bool; stdcall;
      begin
        Result := True;
        Windows.UpdateWindow(hWnd);
      end;
    begin
      EnumWindows(@UpdateWindow, 0);
    end;
    

    【讨论】:

    • 谢谢 - 我会记住这一点。目前,问题已通过 .Refresh 解决(我必须直接在要刷新的组件上执行它,而不是在包含该组件的表单上)。
    【解决方案5】:

    您是否有可能在子窗体中执行某些操作来阻止消息?是否添加:

    Application.ProcessMessages;
    

    对你的代码有什么影响?

    【讨论】:

      【解决方案6】:

      我猜你的模态表单可能会阻止消息。如果您处于某种循环中,则进行某种处理以尝试使用进度更新主窗体。 Application.ProcessMessages 是处理这些消息的一种方式,但恕我直言不是很优雅。当我之前遇到过这类问题时,我已经在 Application.OnIdle 事件中实现了处理。基本上,您需要做的是将您的处理分成小块。因此,假设您正在处理一些循环。将循环的一次迭代作为任务的一部分。将该代码放入具有以下签名的方法中:

      procedure DoIdle(Sender: TObject; var Done: Boolean);
      

      确保完成设置为 False。如果您的代码以前是:

      for i := 1 to ProcessCount do
        DoProcess(i);
      

      这就变成了:

      procedure MyDoIdle(Sender: TObject; var Done: Boolean);
      begin
        Inc(TaskCount);
        If TaskCount <= ProcessCount then
          DoProcess(TaskCount);
      end;
      

      并设置如下:

      TaskCount := 0;
      Application.Idle := MyDoIdle;
      

      然后,只要应用程序空闲,代码就会运行,并且消息循环会正常处理。完成后记得将 Application.OnIdle 设置为 nil。

      如果 DoProcess 有点太快,您可以选择每次调用 onidle 进行 5 或 10 次迭代。

      【讨论】:

      • 谢谢 - 但这与一些后台任务/线程无关。用户只是更改了全局数据并立即生效,因为当模式表单关闭时我无法取消它。更改后的全局数据应重新绘制当前显示它的主窗体。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-31
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多