我的Windows窗体应用程序中有以下程序流程(不幸的是WPF不是一个可行的选项):
Form
。Application.Run()
。async
事件处理程序执行初始化,使用IProgress
界面将进度报告回GUI。 (这完美无瑕。)此时我陷入困境:我知道我需要让GUI线程为我创建这些组件,但没有Control
我可以调用InvokeRequired
。做MainWindow.InvokeRequired既不起作用。
我能想到的唯一想法是触发连接到GUI线程中的工厂的事件,然后等待该工厂触发另一个提供创建的控件的事件。但是我很确定有一个更强大的解决方案。有谁知道如何实现这个目标?
答案 0 :(得分:0)
使用我的问题的评论,特别是有关使我找到this very useful question的延续方法的说明,我实现了以下目标:
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();
}
答案 1 :(得分:0)
管理多个表单并在另一个表单工作或构建时显示一个表单要容易得多。
我建议您尝试以下方法:
启动应用程序后,您可以创建启动画面,以便您的Program.cs就像这样
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new SplashForm());
}
在splash表单构造函数中,创建一个新线程(我将使用BackgroundWorker,但还有其他选项,如任务)来构建主表单。
public SplashForm()
{
InitializeComponent();
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
backgroundWorker1.RunWorkerAsync();
}
现在我们需要编写SplashForm成员函数来告诉后台工作者该做什么
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
// Perform non-GUI initialization - The GUI thread will be responsive in the meantime
// My time consuming operation is just this loop.
//make sure you use worker.ReportProgress() here
for (int i = 1; (i <= 10); i++)
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
break;
}
else
{
System.Threading.Thread.Sleep(500);
worker.ReportProgress((i * 10));
}
}
SetVisible(false);
MainForm mainForm = new MainForm();
mainForm.ShowDialog();
//instead of
//this.Visible = false;
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
}
您可能已经注意到,我正在使用其他成员函数来隐藏启动画面。这是因为你现在在另一个主题中,你不能只使用this.visible = false;
。这是关于此问题的link。
delegate void SetTextCallback(bool visible);
private void SetVisible(bool visible)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetVisible);
this.Invoke(d, new object[] { visible });
}
else
{
this.Visible = visible;
}
}
当我运行此示例项目时,它会显示进度条,然后在隐藏SplashForm后加载MainForm窗体。
这样,您可以在MainForm构造函数中放置您可能需要的任何控件。您缩写为// Perform GUI initialization afterwards in the UI context
的部分应该进入MainForm构造函数。
希望这有帮助。