winforms 创建表单后,InvokeRequired返回false

4uqofj5v  于 2023-04-07  发布在  其他
关注(0)|答案(1)|浏览(110)

这个问题是关于如何在UI线程上重新执行-InvokeRequired返回false,而我认为它应该返回true。我有一个简单的Windows窗体应用程序用于测试和调试。将行消息写入VStudio中的输出窗口以跟踪线程和操作。
我的目标(或应用架构)是让所有的动作初始化(菜单或其他调用者/发起者)都通过一个接口层,该接口层为调用线程提供异步/等待服务,以便在接口工作线程和方法完成工作时释放它们。
问题是InvokeRequired测试返回false。当我跳过测试,只调用Invoke((Action) FormCloseDutiesHelper)时,错误消息说控件的句柄(我想是Form 1)还没有创建。但是当然,Form 1已经创建了-我正在单击File/Close菜单项!
我读了很多关于InvokeRequired和优化Code Pattern的SO文章,但是我没有发现任何东西可以解释为什么InvokeRequired认为“the”控件没有句柄。鼠标放在“this.InvokeRequired”上说“this”是MyDemoProgram.Form1对象。我不知道怎么解释这是怎么回事。
下面是我使用的测试代码,从菜单中单击“代码”。

Program.cs:
 internal static class Program
    {
      public static readonly Form1 PublicForm1 = new();

Form1.cs:
  public partial class Form1 : Form
  {
  
  public Form1() {
    InitializeComponent();
  }

  async void closeToolStripMenuItem_Click(object sender, EventArgs e) {
    var me = "CloseMenuItemClick";
    var msg = $"{me} Thread id {Thread.CurrentThread.ManagedThreadId}";
    Debug.WriteLine(msg);

    // this frees the UI thread up for other work - and it does, as shown by the trace below
    await TaskHelperClose();
  }

Interface.cs:
      public static async Task<bool> TaskHelperClose() {
        var me = "TaskHelperClose";
        var msg = $"{me} Thread id {Thread.CurrentThread.ManagedThreadId}";
        Debug.WriteLine(msg);

        // this lets the calling thread go away immediately
        await Task.Run(HelperClose);

        // just so that the Task does not return void
        return true;
      }

Interface.cs:
  // this runs on the new worker thread
  internal static void HelperClose() {
    var me = MethodBase.GetCurrentMethod()?.Name;
    var msg = $"{me} Thread id {Thread.CurrentThread.ManagedThreadId}";
    Debug.WriteLine(msg);

    // close means put the window in the tray
    // FormCloseCaller tests InvokeRequired to get the work done on the UI thread
    PublicForm1.FormCloseCaller();
  }

Form1.cs:
  // this is still running on the worker thread
  internal void FormCloseCaller() {
    var me = MethodBase.GetCurrentMethod()?.Name;
    var msg = $"{me} Thread id {Thread.CurrentThread.ManagedThreadId}";
    Debug.WriteLine(msg);

    // this is supposed to return true for the menu I just clicked on, but it does not.
    if (this.InvokeRequired) {
      Debug.Writeline($"{me} Invoke required.");
      Invoke((Action) FormCloseDutiesHelper);
    }
    else {
      Debug.Writeline($"{me} Invoke NOT required.");
      FormCloseDutiesHelper();
    }
  }

Form1.cs:
 void FormCloseDutiesHelper() {
    var me = MethodBase.GetCurrentMethod()?.Name;
    var msg = $"{me} Thread id {Thread.CurrentThread.ManagedThreadId}";
    Debug.WriteLine(msg);

    msg = $"{me} Thread id {Thread.CurrentThread.ManagedThreadId}" 
          + " Setting window state to minimized";
    Debug.WriteLine(msg);

    WindowState = FormWindowState.Minimized;
    ShowInTaskbar = false;

    notifyIcon1.Text = @"Click or double click to open";
    notifyIcon1.Visible = true;
  }

下面是执行中线程的轨迹。UI线程是1,await/async/worker线程是9,Invoke不是必需的(即使worker线程不是UI线程),表单没有最小化。

CloseMenuItemClick Thread id 1 - the UI thread
TaskHelperClose Thread id 1

HelperClose Thread id 9        - the await Task.Run(HelperClose) thread
FormCloseCaller Thread id 9    - this guy tests InvokeRequired
FormCloseCaller Invoke NOT required. - and concludes that Invoke is NOT required

FormCloseDutiesHelper Thread id 9  - and used the worker thread to call the helper code
FormCloseDutiesHelper Thread id 9 Setting window state to minimized
- and the window was not minimized.

我也没有得到跨线程异常,除非我跳过InvokeRequired测试并直接调用FormCloseDutiesHelper方法。
有谁能解释一下这是怎么回事,以及我如何在原始UI线程上完成工作?谢谢。

gwbalxhn

gwbalxhn1#

正如@KlausGutter所建议的,我的程序中确实有两个表单。在他发布答案的同时,我发现了这个问题。下面是违规代码。我使用PublicForm 1使表单对其他类可见,当我定义它时,我忘记将名称放入下面显示的Application.Run(new Form1());语句中。一旦我将PublicForm 1放入Run语句中,一切都开始按预期工作。

internal static class Program
    {
      public static readonly Form1 PublicForm1 = new();

        /// <summary>
        ///  The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
          
            // To customize application configuration such as set high DPI settings or default font,
            // see https://aka.ms/applicationconfiguration.
            ApplicationConfiguration.Initialize();
            Application.Run(new Form1());

相关问题