我很难找到是否可以从另一个元素对 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();
}
当我在“调试运行”中单击按钮时,VS2019 会中断,并显示“InvalidOperationException:跨线程操作无效:从创建它的线程以外的线程访问控制‘radio_button_special’。”
但是,如果我只是在 Visual Studio 调试之外运行 exe,该按钮似乎可以工作(即我可以在应用程序中看到 radio_button_special.PerformClick() 首先完成,然后 do_rest_of_processing() 运行 - 并且我没有得到明确的异常像这样运行,所以它似乎按照我的想象工作)。
但这个异常让我害怕,所以我想摆脱它 - 我已经尝试过像这样的补救措施,我尝试从其他片段构建:
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();
}
现在,这个函数已经在VS2019 IDE中给出了警告:
警告 CS1998:此异步方法缺少“等待”运算符,并将同步运行。考虑使用“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# 表单组件处理程序(例如单击)函数,等待它完成,然后调用另一个函数 - 而不会引发异常?
如果您只想从一种方法调用另一种方法,那么这可以工作:
Task.Run
队列工作到线程池,这在这里是不正确的。如果您有很长的工作要做,那么在线程池线程上运行该工作对您的用户来说会更好。在这种情况下,正确的解决方案是重构代码,以便有另一种方法可以通过单选按钮单击和此按钮单击来调用。
Task.Run
将您提供的方法排队以在线程池线程上运行。因此,即使您执行调用,您仍然可以从另一个线程访问该控件。相反,您应该将Control.Invoke
内部称为Task.Run
.编辑:对 OP 海报有用的是: