winforms 可以从另一个组件启动并等待表单组件的PerformClick吗?

uwopmtnx  于 2023-10-23  发布在  其他
关注(0)|答案(2)|浏览(111)

我很难找到是否可以从另一个元素在C#表单元素上执行虚拟单击,并等待它完成后再继续。
作为一个例子(不幸的是不完整),我想做的是,

private async void btn_my_Click(object sender, EventArgs e)
  {
      await Task.Run(() => radio_button_special.PerformClick()); // https://stackoverflow.com/q/14024963

      // once this is done, proceed with rest of processing
      do_rest_of_processing();
  }

当我在运行中单击按钮时,VS 2019会出现“InvalidOperationException:跨线程操作无效:控件'radio_button_special'从创建它的线程以外的线程访问。”
但是,如果我只是在Visual Studio debug之外运行exe,按钮似乎可以工作(即,我可以在应用程序中看到,radio_button_special.PerformClick()首先完成,do_rest_of_processing()在之后运行-我没有得到像那样运行的显式异常,所以它似乎像我想象的那样工作)。
但是这个异常让我害怕,所以我想摆脱它-我已经尝试过这样的补救措施,我已经尝试过从SO上的其他片段构建:

private async void btn_my_Click(object sender, EventArgs e)
  {
      // first, I want to call the function otherwise called
      // when the radio button is clicked, and wait for it to complete
      radio_button_special.Invoke((MethodInvoker)async delegate
      {
          await Task.Run(() => radio_button_special.PerformClick()); // https://stackoverflow.com/q/14024963
      });
      // once this is done, proceed with rest of processing
      do_rest_of_processing();
  }

现在,这个函数已经在VS 2019 IDE中给出了警告:
警告CS 1998:此异步方法缺少'await'运算符,将同步运行。考虑使用“await”运算符等待非阻塞API调用,或使用“await Task.Run(...)"在后台线程上执行CPU绑定的工作。
.如果我调试运行示例,我会得到堆栈跟踪:

System.Reflection.TargetInvocationException
  HResult=0x80131604
  Message=Exception has been thrown by the target of an invocation.
  Source=mscorlib
  StackTrace:
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor)
   ...
   at System.Windows.Forms.Application.Run(Form mainForm)
   at my_test.Program.Main() in D:\work\bbs\BBS_DEV\BAPS_git\my_test\Program.cs:line 28

  This exception was originally thrown at this call stack:
    System.Windows.Forms.Control.Handle.get()
    System.Windows.Forms.Control.InternalHandle.get()
    System.Windows.Forms.Control.Update()
    System.Windows.Forms.ButtonBase.ResetFlagsandPaint()
    System.Windows.Forms.RadioButton.PerformClick()
    my_test.main_form.btn_my_Click.AnonymousMethod__122_1() in main_form.cs
    System.Threading.Tasks.Task.InnerInvoke()
    System.Threading.Tasks.Task.Execute()
    System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(System.Threading.Tasks.Task)
    System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task)
    ...
    [Call Stack Truncated]

Inner Exception 1:
InvalidOperationException: Cross-thread operation not valid: Control 'radio_button_special' accessed from a thread other than the thread it was created on.

所以,基本上和之前一样的问题。
那么,我如何调用C#表单组件处理程序(例如,单击)函数,等待它完成,然后调用另一个函数-而不会引发异常?

bvjveswy

bvjveswy1#

如果你只是想从另一个方法调用一个方法,那么这将起作用:

private void btn_my_Click(object sender, EventArgs e)
{
  radio_button_special.PerformClick();
  do_rest_of_processing();
}

Task.Run队列在线程池中工作,这里是不正确的。
如果您有较长的工作要做,那么在线程池线程上运行该工作对用户更好。在这种情况下,正确的解决方案是重构代码,以便有另一个方法可以由单选按钮单击和此按钮单击调用。

uajslkp6

uajslkp62#

Task.Run将给予在线程池线程上运行的方法排队。因此,即使执行了调用,您仍在从另一个线程访问控件。相反,您应该在Task.Run中调用Control.Invoke

private async void button1_Click(object sender, EventArgs e)
{
    await Task.Run(() =>
    {
        radioButton1.Invoke(() => radioButton1.PerformClick());
    });
}

编辑:OP海报的工作是:

private async void button1_Click(object sender, EventArgs e)
{
    await Task.Run(() =>
    {
        radioButton1.Invoke((MethodInvoker) delegate { radioButton1.PerformClick(); });
    });
}

相关问题