c# - 如何要求 GUI 线程创建对象?

标签 c# multithreading winforms async-await

我的 Windows 窗体应用程序中有以下程序流(不幸的是,WPF 不是一个可行的选项):

  1. GUI 线程创建一个初始屏幕和一个非常空的主窗口,两者都继承了 Form
  2. 启动画面显示并提供给 Application.Run()
  3. 初始屏幕将发送一个事件,该事件触发一个执行初始化的 async 事件处理程序,使用 IProgress 接口(interface)向 GUI 报告进度。 (这完美无缺。)​​
  4. 在初始化过程中的某个时候,我需要根据某些插件提供的信息动态创建 GUI 组件并将它们添加到主窗口。

此时我卡住了:我知道我需要让 GUI 线程为我创建这些组件,但是没有 Control 我可以调用 InvokeRequired在。执行 MainWindow.InvokeRequired 都不起作用。

我能想到的唯一想法是触发一个连接到 GUI 线程中的工厂的事件,然后等待该工厂触发另一个提供已创建控件的事件。但是我很确定有一个更强大的解决方案。有谁知道如何实现这一点?

最佳答案

使用对我的问题的评论,尤其是关于让我找到 this very useful question 的延续方法的注释,我实现了以下目标:

  • 初始化的第一部分是异步执行的(没有变化)。
  • 初始化的第二部分(创建 UI 元素)随后作为延续任务在 UI 线程的上下文中执行。
  • 除了相当短的 GUI 初始化部分外,初始屏幕是响应式的(即鼠标光标悬停在初始屏幕上后不会变为“等待”)。
  • 两个初始化例程都不知道启动画面(也就是说,我可以很容易地交换它)。
  • 核心 Controller 只知道SplashScreen界面,甚至不知道它是一个Control
  • 目前没有异常处理。这是我的下一个任务,但不影响这个问题。

TL;DR:代码看起来有点像这样:

public void Start(ISplashScreen splashScreen, ...)
{
    InitializationResult initializationResult = null;
    var progress = new Progress<int>((steps) => splashScreen.IncrementProgress(steps));
    splashScreen.Started += async (sender, args) => await Task.Factory.StartNew(

             // Perform non-GUI initialization - The GUI thread will be responsive in the meantime.
             () => Initialize(..., progress, out initializationResult)

        ).ContinueWith(

            // Perform GUI initialization afterwards in the UI context
            (task) =>
                {
                    InitializeGUI(initializationResult, progress);
                    splashScreen.CloseSplash();
                },
            TaskScheduler.FromCurrentSynchronizationContext()

        );

    splashScreen.Finished += (sender, args) => RunApplication(initializationResult);

    splashScreen.SetProgressRange(0, initializationSteps);        
    splashScreen.ShowSplash();

    Application.Run();
}

关于c# - 如何要求 GUI 线程创建对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30916002/

相关文章:

C# SecuGen 加载回指纹图像进行验证

java - Java中的ScheduledExecutorService和线程

c# - ASP.NET 从服务器获取文件列表

c# - 空检查何时可以引发NullReferenceException

c# - 使用 SslStream 在 TLS session 上向客户端发送关闭通知

java - 线程填充集合和Java内存模型

c# - 异步请求上的共享 HttpClient block

.NET:如何将 Windows 窗体转换为 Windows 控件

c# - 更新数据源后,Telerik 的 RadListView 未绘制项目

c# - 带有保存按钮的 WinForms 数据绑定(bind)?