C# 如何在新线程中加载windows窗体?

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

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 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。