C# 如何在新线程中加载windows窗体?
c.NETWinForms,IDE:VS2010 我有两个窗口从F1,F2 F1是调用方,F2是我想在线程中加载的表单,因为它上面有很多丰富的控件 我能够在子线程中加载f2,但它只是变得可见并运行,因为它在子线程中。参见案例1代码 案例1代码C# 如何在新线程中加载windows窗体?,c#,multithreading,winforms,C#,Multithreading,Winforms,c.NETWinForms,IDE:VS2010 我有两个窗口从F1,F2 F1是调用方,F2是我想在线程中加载的表单,因为它上面有很多丰富的控件 我能够在子线程中加载f2,但它只是变得可见并运行,因为它在子线程中。参见案例1代码 案例1代码 private void button1_Click(object sender, EventArgs e) { StartProgress(); Thread th = new Thread(new ThreadS
private void button1_Click(object sender, EventArgs e)
{
StartProgress();
Thread th = new Thread(new ThreadStart(LoadForm));
th.Start();
}
private void StartProgress()
{
progressBar1.Maximum = 100;
progressBar1.Step = 1;
for (int i = 0; i <= 100; i++)
{
label1.Text = i + "%";
progressBar1.PerformStep();
Thread.Sleep(10);
label1.Refresh();
}
}
private void LoadForm()
{
Form2 f2 = new Form2();
f2.Show();
}
}
然后我做了相反的操作,我在子线程中加载了进度条,在主线程中加载了f2
//代码案例2:
情况2:进度条位于子线程上时
private void button1_Click(object sender, EventArgs e)
{
LoadForm();
Thread th = new Thread(new ThreadStart(StartProgress));
th.Start();
LoadForm();
}
private void StartProgress()
{
progressBar1.Maximum = 100;
progressBar1.Step = 1;
for (int i = 0; i <= 100; i++)
{
label1.Text = i + "%";
progressBar1.PerformStep();
Thread.Sleep(10);
label1.Refresh();
}
}
private void LoadForm()
{
Form2 f2 = new Form2();
f2.Show();
}
但是案例2有两个问题
问题1:它像往常一样通过轻弹加载f2。
问题2:交叉线程操作进度Bar1
//请建议如何在后台加载f2,并在加载进度条后显示它。您不应该有多个用户界面UI线程。在接口之间保存数据的复杂性将非常困难,并且通常会使应用程序不具有线程安全性。这将为您带来一个又一个错误 主要目标应该是让用户界面UI线程响应。通过这种方式,当用户与应用程序交互时,当应用程序执行计算任务时,界面会做出响应 这是一篇关于保持UI线程响应性的好文章 您可能需要注意Microsoft在此处的说明: 注意:并非所有对UI的更改都必须在UI线程上完成。 有一个单独的渲染线程,可以应用不会改变的UI更改 影响如何处理输入或基本布局。比如很多 通知用户UI操作已完成的动画和过渡 可以在该渲染线程上实际运行。但如果是你的 在页面的某个地方更改UI元素的代码,这是最好的 假设您的代码可能会阻止UI 线程,除非您熟悉这些API或子系统并且知道 当然,它们不会影响UI线程 更新:
我不知道您使用的是Windows窗体,上面列出的文章是针对XAML的。您可以使用本文帮助为Windows窗体构建具有计算任务的响应式UI。链接是针对WPF的,您需要为Windows窗体进行移植。如果您只想避免闪烁,请创建F2,并仅在初始化完成后显示它
我已经有一段时间没有做任何Windows窗体编程了,但我想您可以在_loadevent handler中显示窗体。如果需要在后台加载辅助窗口,更好的方法是在当前UI的后台加载数据,然后将该信息传递给新窗口
public void btnNewWindow_Click(object sender, EventArgs args)
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += WorkerOnDoWork;
worker.ProgressChanged += WorkerOnProgressChanged;
worker.RunWorkerCompleted += WorkerOnRunWorkerCompleted;
worker.RunWorkerAsync();
}
private List<string> _data = new List<string>();
private void WorkerOnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs runWorkerCompletedEventArgs)
{
//Work has finished. Launch new UI from here.
Form2 f2 = new Form2(_data);
f2.Show();
}
private ProgressBar progressBar1;
void WorkerOnProgressChanged(object sender, ProgressChangedEventArgs e)
{
//Log process here
}
private void WorkerOnDoWork(object sender, DoWorkEventArgs e)
{
//Perform work you need to load the data.
for (int i = 0; i <= 100; i++)
{
Thread.Sleep(50);
_data.Add("Test" + i);
}
}
我在WPF中很快就把它们组合在一起了,但是您应该能够使用相同的原则来实现这一点。我将重申,您不应该使用第二个UI线程。预先加载数据,然后在准备就绪且可用时传递到窗口。您不想这样做。拥有多个UI线程是一个非常糟糕的主意。您应该只有一个UI线程,并且您应该异步执行非UI工作,以便不阻止它,而不是阻止您的UI线程并尝试通过第二个UI线程进行补偿。我只想在加载时删除F2中的flicking,我只想在后台加载F2。在进度条填满之前,我想在屏幕上显示加载的f2,我告诉你,你应该重新设计你的应用程序,这样你就不会有多个UI线程,并避免首先阻塞你的主UI线程。当您这样做时,您的程序将更简单、更易于编写/理解,并且将正常运行。与其给你一个水桶来把这艘正在下沉的船里的水弄出来,我建议你买一艘没有洞的船!然而,@Servy和其他人是对的。这实际上是不可能的,如果不是完全不可能的话。将后台工作移动到后台线程,然后在UI线程中加载表单。添加了一个示例链接和一些解释。我应用了它,减少了20%的翻转,但80%仍然存在。何时发生?在隐藏窗体之前还是在使窗体再次可见之后?感谢您的努力,我将在需要大量数据加载的情况下使用此概念,但我的Form2 F2包含大量用户控件,如自定义多边形、按钮等,因此有一次在加载F2时设计这些控件时,它会在屏幕上闪烁,因此我想用线程隐藏这种情况。如果您有其他解决方案,欢迎使用。@user3593678那么,将表单拆分为单独的选项卡并在应用程序中使用有限的空间如何?让用户选择不同的选项卡,这样就不必一次加载所有UI。