我的 Windows 窗体应用程序中有以下程序流(不幸的是,WPF 不是一个可行的选项):
- GUI 线程创建一个初始屏幕和一个非常空的主窗口,两者都继承了
Form
。 - 启动画面显示并提供给
Application.Run()
。 - 初始屏幕将发送一个事件,该事件触发一个执行初始化的
async
事件处理程序,使用IProgress
接口(interface)向 GUI 报告进度。 (这完美无缺。) - 在初始化过程中的某个时候,我需要根据某些插件提供的信息动态创建 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/