c# - 当我从不使用 Invoke 或 BeginInvoke 时,不能调用 Invoke 或 BeginInvoke 错误
问题描述
我是 C# 和异步编程的新手,所以我遇到的问题的解决方案可能很简单(或者不是?!)。
这是问题:
- 我有一个 onClick 函数,它创建一个新的 UIControl 并将其作为新选项卡添加到当前表单中。
- 我首先让它同步运行并且没有崩溃,但是 UI 被阻塞了几秒钟,因为提供给此选项卡的数据需要大量计算。
- 我将 onclick 移动到异步并在 Control 的数据处理功能上添加了等待,但现在我得到了臭名昭著的:
“在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke”。
private async void btnNewTab_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Multiselect = true;
openFileDialog.Filter = "*.test";
DialogResult dialogResult = openFileDialog.ShowDialog();
if (dialogResult == DialogResult.OK)
{
//Display a basic loading Control while the other control is being prepared
TabPage myTabPage = new TabPage("loading");
LoadingControlPanel loadingPanel = new LoadingControlPanel();
loadingPanel.Dock = DockStyle.Fill;
myTabPage.Controls.Add(loadingPanel);
tabControl.TabPages.Add(myTabPage);
tabControl.SelectTab(myTabPage);
//Load Data from files
List<DataFile> result = await Task.Run(() => loadFiles(openFileDialog.FileNames));
// create Control to display the data loaded
MyTabControl newPanel = new MyTabControl();
newPanel.Dock = DockStyle.Fill;
// provide Data to new control (long processing)
await Task.Run(() => newPanel.processData(result));
// rename Tab, remove loading Control and display the tab with the processed Data
myTabPage.Text = "Loaded";
myTabPage.Controls.Remove(loadingPanel);
myTabPage.Controls.Add(newPanel);
}
}
当我调用函数“processData”但并不总是在同一个位置时,崩溃总是发生。我有几条调试线,它们的处理方式并不总是相同的。
崩溃告诉我问题就行了:“Application.Run(new FormMain());”
static void Main()
{
Application.SetHighDpiMode(HighDpiMode.SystemAware);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new FormMain());
}
这是完整的堆栈跟踪:
System.Reflection.TargetInvocationException HResult=0x80131604
Message=异常已被调用的目标抛出。
Source=System.Private.CoreLib StackTrace: 在 System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder , Object[] 参数, CultureInfo 文化)
在 System.Delegate.DynamicInvokeImpl(Object[] args) 在 System.Delegate.DynamicInvoke(Object[] args) 在 System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry tme) 在 System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object obj) 在 System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) --- 从先前位置结束堆栈跟踪 --- 在 System.Threading 的 System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()。 System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry tme) 在 System.Windows.Forms.Control.InvokeMarshaledCallbacks() 的 ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
在 System.Windows.Forms.Control.WndProc(Message& m) 在 System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
在 System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, WM msg, IntPtr wparam, IntPtr lparam) 在 Interop.User32.DispatchMessageW(MSG& msg) 在 System.Windows.Forms.Application.ComponentManager.Interop.Mso.IMsoComponentManager.FPushMessageLoop(UIntPtr dwComponentID, msoloop uReason, Void* pvLoopData) 在 System.Windows.Forms.Application .ThreadContext.RunMessageLoopInner(msoloop reason, ApplicationContext context) at System.Windows.Forms.Application.ThreadContext.RunMessageLoop(msoloop reason, ApplicationContext context) at System.Windows.Forms.Application.Run(Form mainForm) at Program.Main()在 Program.cs:第 22 行此异常最初是在此调用堆栈中引发的:[外部代码]
内部异常 1:InvalidOperationException:在创建窗口句柄之前,无法在控件上调用 Invoke 或 BeginInvoke。
我到底做错了什么?
解决方案
问题是Task.Run(() => newPanel.processData(result))
,这只能在 processData() 没有触及任何 UI 内容时起作用(但为什么它会成为面板的一部分?)
您只能使用 Task.Run() 处理内容,然后在主线程的 UI 中加载结果。
看看你能不能让它看起来像:
//await Task.Run(() => newPanel.processData(result));
var data = await Task.Run(() => ProcessData(result));
newPanel.AcceptData(data);
LoadData 和 ProcessData() 最好是某些服务类的一部分,而不是您的表单。但这与架构有关,而不是直接问题。
推荐阅读
- c# - 使用 SQLite 显示当前数据库中的所有表
- python - 为什么这两个 API(twitter 地理/搜索 API)返回不同的结果集?
- python - 在 Window OS 中双击时如何在 Idle shell 中运行脚本?
- java - GUI 对象不绘制
- c# - 使用 IEnumerable 进行 C# 三元转换
- r - 如何从迭代交叉验证计算 lambda.1se?
- java - IntelliJ 警告:为 varargs 参数创建未经检查的泛型数组
- valgrind - 如何检测 valgrind 中寄存器 sp 的修改
- typescript - 匹配 Typescript 中可能的类型文字
- c++ - 使用模板参数编译时间码扩展