首页 > 解决方案 > 未显示表单时,如何在不同的线程/任务中处理 Windows.Forms 的初始化?

问题描述

我有一个 C# .NET Framework 4.7 应用程序,其中的主窗体需要一些时间来加载(数据库查询等)。

为了通知用户应用程序正在运行和加载,我实现了一个启动画面。

但是,我无法设法使 MainForm 初始化(数据库查询等)工作。

如果我将初始化代码放入 MainForm 构造函数中,并放入mainForm = new MainForm()主线程/任务中,由于操作是同步的,因此启动屏幕会冻结,因为没有 DoEvent()。

如果我将初始化代码放入 MainForm 方法中,并mainForm = new MainForm()如下所示放入,然后将初始化如下所示,我将在InvalidOperationException尝试访问另一个线程/任务中的组件时以 s 结束。

当我尝试Invoke在任务中解决此异常时(如下所示),我最终会InvalidOperationException抱怨在Invoke未显示表单时无法调用(未创建窗口句柄)。我尝试在调用之前创建窗口句柄Invoke,但遇到了同样的异常,因为必须显示表单才能显示Invoke任何内容。

如何在单独的线程/任务中初始化 MainForm,以便多个初始化任务可以在 Main 函数中并行发生?

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

internal static class Program
{
    [STAThread]
    private static int Main()
    {
        // Display the splash screen.
        var splashScreen = new SplashScreenForm()
        {
            ApplicationTitle = Program.Information.EntryAssembly.Title,
            ApplicationVersionInformation = string.Format("Version {0} ({1})", Program.Information.EntryAssembly.InformationalVersion, Program.Information.EntryAssembly.Configuration),
            Copyright = Program.Information.EntryAssembly.Copyright
        };
        splashScreen.Show();

        var initTasks = new List<Task>();
        var mainForm = new MainForm();

        // Load main form.
        {
            var initTask = new Task(() => { mainForm.Initialize(); });
            initTasks.Add(initTask);
            initTask.Start();
        }

        // Some other initialization tasks, that are NOT related to MainForm.
        {
            var initTask = new Task(() => { Console.WriteLine("Foo Bar") });
            initTasks.Add(initTask);
            initTask.Start();
        }

        // Wait until all initialization tasks are done.
        while (initTasks.Any((Task t) => { return !Convert.ToBoolean(t.IsCanceled | t.IsFaulted | t.IsCompleted); }))
        {
            Thread.Sleep(10);
            Application.DoEvents();
        }

        // Check for failed initialization tasks.
        if (initTasks.Any((Task t) => { return Convert.ToBoolean(t.IsCanceled | t.IsFaulted); }))
        {
            var exes = new List<Exception>();
            foreach (var t in initTasks.FindAll((Task t) => { return Convert.ToBoolean(t.IsCanceled | t.IsFaulted); }))
            {
                exes.Add(t.Exception);
            }
            throw new AggregateException("Some main initialization tasks have failed.", exes.ToArray());
        }

        // Cleanup tasks.
        initTasks.ForEach((Task t) => { t.Dispose(); });
        initTasks.Clear();
        splashScreen.Close();
        splashScreen.Dispose();

        Application.Run(mainForm);
        Application.Exit();
        Application.DoEvents();
        return 0;
    }
}

标签: c#multithreadingtasksplash-screen

解决方案


推荐阅读