当我从不使用 Invoke 或 BeginInvoke 时,无法调用 Invoke 或 BeginInvoke 错误

Invoke or BeginInvoke cannot be called Error when I never use Invoke or BeginInvoke

我是 C# 和异步编程的新手,所以我遇到的问题的解决方案可能很简单(或者不是?!)。

这是问题所在:

"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 已更改和改进以避免需要此解决方案。