Как обработать инициализацию Windows.Forms в другом потоке / задаче, пока форма не отображается? - PullRequest
1 голос
/ 15 марта 2019

У меня есть приложение на 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;
    }
}
...