在没有实际表单显示的情况下进入表单模态消息循环

本文关键字:表单 模态 循环 消息 情况下 显示 | 更新日期: 2023-09-27 17:49:13

我有一个WinForms应用程序,其中一个主要形式是MainForm。这个应用程序在一定的时间间隔内调用了一些预定的操作。这个操作需要很多时间来执行,所以它是在后台线程中启动的。运行时显示有进度的表格- LoaderForm。该形式通过loader.ShowDialog(mainForm)表示为模态;

这个工作正常,除非操作开始时:

  • MainForm在任务栏被最小化
  • MainForm隐藏在托盘中(根本不显示在任务栏中)
在这种情况下,我需要而不是来显示LoaderForm,除非应用程序被激活。问题是,即使MainForm在没有适当处理的情况下在视觉上不可见,LoaderForm也会显示出来。

实现预期行为的最简单方法是什么?


示例代码

Imports System.ComponentModel

Public Class MainForm
    Private Sub Timer_Tick(sender As Object, e As EventArgs) Handles uiTimer.Tick
        uiTimer.Enabled = False
        Using loader = New LoaderForm()
            loader.ShowDialog(Me)
        End Using
    End Sub
End Class
Public Class LoaderForm
    Private Sub LoaderForm_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
        worker.RunWorkerAsync()
    End Sub
    Private Sub Worker_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs) Handles worker.RunWorkerCompleted
        Me.Close()
    End Sub
End Class

在没有实际表单显示的情况下进入表单模态消息循环

我们可以通过调用未记录的 Win32 api BeginModalMessageLoopEndModalMessageLoop来创建一个新的模态消息循环,而无需实际显示模态对话框。

注意:这些api没有文档记录,所以我们不应该实际使用它们。

    //Capture method reference using reflection
    _methodBeginLoop = FindMethod(typeof (Application), "BeginModalMessageLoop");
    _methodEndLoop = FindMethod(typeof (Application), "EndModalMessageLoop");
       private static MethodInfo FindMethod(Type type, string methodName)
            {
                const BindingFlags BINDING_FLAGS = BindingFlags.Static | BindingFlags.NonPublic;
                MemberInfo[] members = type.FindMembers(MemberTypes.Method, BINDING_FLAGS,
                    new MethodFilter(methodName).Matches, null);
                if (members.Length == 1)
                    return members[0] as MethodInfo;
                return null;
            }

对于阻塞主表单上的输入,可以使用

Application.OpenForms[0].Invoke(new Action(() =>
 {
   _methodBeginLoop.Invoke(null, null);
 }

对于UnBlocking在main form上的输入,可以使用

Application.OpenForms[0].Invoke(new Action(() =>
 {
   _methodEndLoop.Invoke(null, null);
 }