如何在C#中的不同线程上运行新表单?

时间:2010-08-14 10:28:40

标签: c# .net winforms multithreading message-loop

我只是尝试在每次按钮点击时都运行一个新线程,这应该创建一个新表单。我在MainForm中的按钮单击事件中尝试了这个:

private void button1_Click(object sender, EventArgs e)
{
    worker1 = new Thread(new ThreadStart(thread1));
    worker2 = new Thread(new ThreadStart(thread2));

    worker1.Start();
    worker2.Start();
}

private void thread1()
{
    SubForm s = new SubForm();
    s.Show();
}

private void thread2()
{
    SubForm s = new SubForm();
    s.Show();
}

子窗体按钮单击事件中的代码如下所示:

private void button1_Click(object sender, EventArgs e)
{
    int max;
    try
    {
        max = Convert.ToInt32(textBox1.Text);
    }
    catch
    {
        MessageBox.Show("Enter numbers", "ERROR");
        return;
    }

    progressBar1.Maximum = max;

    for ( long i = 0; i < max; i++)
    {
        progressBar1.Value = Convert.ToInt32(i);
    }          
}

这是正确的方法吗?因为我试图打开两个独立的表单,所以一个线程中的操作不应该影响另一个线程。

或者BackGroundworker是实现这个的解决方案吗?如果是的话,有人可以帮助我吗?

4 个答案:

答案 0 :(得分:13)

您不需要在单独的线程中运行表单。您可以通常在多个表单上调用s.Show()。他们不会互相阻挡。

当然,如果你正在做别的东西,比如某种计算或需要很长时间的其他任务,那么你应该单独运行那个线程,但不是形式。

以下是一些代码,可让您创建一个显示长进程进度的进度条。请注意,每次从线程内部访问表单时,都必须使用.Invoke(),它实际上会在GUI线程准备就绪时调度该调用。

public void StartLongProcess()
{
    // Create and show the form with the progress bar
    var progressForm = new Subform();
    progressForm.Show();
    bool interrupt = false;

    // Run the calculation in a separate thread
    var thread = new Thread(() =>
    {
        // Do some calculation, presumably in some sort of loop...
        while ( ... )
        {
            // Every time you want to update the progress bar:
            progressForm.Invoke(new Action(
                () => { progressForm.ProgressBar.Value = ...; }));

            // If you’re ready to cancel the calculation:
            if (interrupt)
                break;
        }

        // The calculation is finished — close the progress form
        progressForm.Invoke(new Action(() => { progressForm.Close(); }));
    });
    thread.Start();

    // Allow the user to cancel the calculation with a Cancel button
    progressForm.CancelButton.Click += (s, e) => { interrupt = true; };
}

答案 1 :(得分:3)

虽然我并不是100%意识到任何事情都表明运行完全独立的表单在自己的线程中执行完全独立的操作是危险的,但在单个线程上运行所有UI操作通常被视为良好的做法。

您可以通过让Subform类使用BackgroundWorker来支持此功能。显示表单后,启动BackgroundWorker,以便它处理您需要的任何内容。

然后,您只需在GUI线程上创建Subform的新实例并显示它们。表单将显示并在另一个线程上开始操作。

这样UI就会在GUI线程上运行,但表单运行的操作将在ThreadPool线程上运行。

<强>更新

以下是您的后台工作人员处理程序的示例 - 注意(像往常一样)这只是我的头脑,但我认为您可以了解基本原则。

将BackgroundWorker添加到名为worker的表单中。将它连接到以下事件处理程序:

void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    // Executed on GUI thread.
    if (e.Error != null)
    {
        // Background thread errored - report it in a messagebox.
        MessageBox.Show(e.Error.ToString());
        return;
    }

    // Worker succeeded.
}

void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    // Executed on GUI thread.
    progressBar1.Value = e.ProgressPercentage;
}

void worker_DoWork(object sender, DoWorkEventArgs e)
{
    // Executed on ThreadPool thread.
    int max = (int)e.Argument;

    for (long i = 0; i < max; i++)
    {
        worker.ReportProgress(Convert.ToInt32(i));
    }
}

您的点击处理程序如下所示:

void button1_Click(object sender, EventArgs e)
{
    int max;

    try
    {
        // This is what you have in your click handler,
        // Int32.TryParse is a much better alternative.
        max = Convert.ToInt32(textBox1.Text);
    }
    catch
    {
        MessageBox.Show("Enter numbers", "ERROR");
        return;
    }

    progressBar1.Maximum = max;

    worker.RunWorkerAsync(max);
}

我希望有所帮助。

答案 2 :(得分:2)

试试这个。它在自己的线程上运行新的Form,它有自己的消息队列,什么不是。 运行以下代码:

new Thread(new ThreadStart(delegate
{
   Application.Run(new Form());
})).Start();

使用Thread.CurrentThread.GetHashCode()来测试它是否在不同的线程上运行。

答案 3 :(得分:1)

可以在不同的线程上运行不同的表单。我知道有两个警告:

  1. 两种形式都不是另一种形式的MDI客户端。当表单具有不同的线程时,尝试使表单成为另一个的MDI客户端将失败。
  2. 如果一个对象将事件发送到多个表单并且所有表单都使用相同的线程,则可以在提升之前将事件同步到主线程。否则,必须异步引发事件,并且每个表单必须为传入事件执行自己的同步机制。

显然,不希望任何窗口的UI线程被阻止,但是为单独的窗口使用单独的线程可能是一个不错的选择。