当我从不使用 Invoke 或 BeginInvoke 时,无法调用 Invoke 或 BeginInvoke 错误
Invoke or BeginInvoke cannot be called Error when I never use Invoke or BeginInvoke
我是 C# 和异步编程的新手,所以我遇到的问题的解决方案可能很简单(或者不是?!)。
这是问题所在:
- 我有一个 onClick 函数,可以创建一个新的 UIControl 并将其作为新选项卡添加到当前窗体。
- 我首先 运行 同步并没有崩溃,但是 UI 被阻塞了几秒钟,因为提供给此选项卡的数据需要大量计算。
- 我将 onclick 移动到异步并在控件的数据处理函数上添加了一个等待,但我现在得到了臭名昭着的:
"Invoke or BeginInvoke cannot be called on a control until the window handle has been created".
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=Exception has been thrown by the target of an invocation.
Source=System.Private.CoreLib StackTrace: at
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[] parameters, CultureInfo culture)
at System.Delegate.DynamicInvokeImpl(Object[] args) at
System.Delegate.DynamicInvoke(Object[] args) at
System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry
tme) at
System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object obj)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext
executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at
System.Threading.ExecutionContext.Run(ExecutionContext
executionContext, ContextCallback callback, Object state) at
System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry
tme) at System.Windows.Forms.Control.InvokeMarshaledCallbacks()
at System.Windows.Forms.Control.WndProc(Message& m) at
System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, WM msg,
IntPtr wparam, IntPtr lparam) at
Interop.User32.DispatchMessageW(MSG& msg) at
System.Windows.Forms.Application.ComponentManager.Interop.Mso.IMsoComponentManager.FPushMessageLoop(UIntPtr
dwComponentID, msoloop uReason, Void* pvLoopData) at
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() in
Program.cs:line
22
This exception was originally thrown at this call stack:
[External Code]
Inner Exception 1: InvalidOperationException: Invoke or BeginInvoke
cannot be called on a control until the window handle has been
created.
我到底做错了什么?
这里是代码
TabControl newPanel = new TabControl();
newPanel.Dock = DockStyle.Fill;
// provide Data to new control (long processing)
await Task.Run(() => newPanel.processData(result));
尝试创建一个新的 TabControl,但从未显示它,因此它从未有关联的 window 句柄。这完全独立于您是同步还是异步处理数据。
您似乎也缺少一些相关代码,因为 System.Windows.Forms.TabControl
没有 processData
方法。
问题是 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() 最好是某些服务的一部分 class,而不是您的表单。但那是关于架构,而不是关于直接问题。
我遇到的问题的核心是创建控件不会自动确保创建连接到它的处理程序。
我更改了我的代码以先显示面板(带有加载消息),同时异步处理和显示数据。
正如@Henk Holterman 所指出的,架构 can/should 已更改和改进以避免需要此解决方案。
我是 C# 和异步编程的新手,所以我遇到的问题的解决方案可能很简单(或者不是?!)。
这是问题所在:
- 我有一个 onClick 函数,可以创建一个新的 UIControl 并将其作为新选项卡添加到当前窗体。
- 我首先 运行 同步并没有崩溃,但是 UI 被阻塞了几秒钟,因为提供给此选项卡的数据需要大量计算。
- 我将 onclick 移动到异步并在控件的数据处理函数上添加了一个等待,但我现在得到了臭名昭着的:
"Invoke or BeginInvoke cannot be called on a control until the window handle has been created".
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=Exception has been thrown by the target of an invocation.
Source=System.Private.CoreLib StackTrace: at 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[] parameters, CultureInfo culture)
at System.Delegate.DynamicInvokeImpl(Object[] args) at System.Delegate.DynamicInvoke(Object[] args) at System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry tme) at System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object obj) at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state) --- End of stack trace from previous location --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) at System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry tme) at System.Windows.Forms.Control.InvokeMarshaledCallbacks()
at System.Windows.Forms.Control.WndProc(Message& m) at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, WM msg, IntPtr wparam, IntPtr lparam) at Interop.User32.DispatchMessageW(MSG& msg) at System.Windows.Forms.Application.ComponentManager.Interop.Mso.IMsoComponentManager.FPushMessageLoop(UIntPtr dwComponentID, msoloop uReason, Void* pvLoopData) at 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() in Program.cs:line 22This exception was originally thrown at this call stack: [External Code]
Inner Exception 1: InvalidOperationException: Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
我到底做错了什么?
这里是代码
TabControl newPanel = new TabControl();
newPanel.Dock = DockStyle.Fill;
// provide Data to new control (long processing)
await Task.Run(() => newPanel.processData(result));
尝试创建一个新的 TabControl,但从未显示它,因此它从未有关联的 window 句柄。这完全独立于您是同步还是异步处理数据。
您似乎也缺少一些相关代码,因为 System.Windows.Forms.TabControl
没有 processData
方法。
问题是 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() 最好是某些服务的一部分 class,而不是您的表单。但那是关于架构,而不是关于直接问题。
我遇到的问题的核心是创建控件不会自动确保创建连接到它的处理程序。
我更改了我的代码以先显示面板(带有加载消息),同时异步处理和显示数据。
正如@Henk Holterman 所指出的,架构 can/should 已更改和改进以避免需要此解决方案。