从另一个表单更新对话框

时间:2012-08-28 22:04:20

标签: c# user-interface dialog thread-safety

我知道有很多类似的问题,我已经阅读了很多。不幸的是,在阅读之后我仍然无法解决我的问题 - 但是我对C#相对较新。根据{{​​3}},在使用调试器时,不是线程安全的问题会导致InvalidOperationException。在我的问题中并非如此。

我用一个简单的原始测试类重新创建了问题,专注于我的问题。

主表单应该显示一种进度对话框。

public partial class ImportStatusDialog : Form
{
    public ImportStatusDialog()
    {
        InitializeComponent();
    }

    public void updateFileStatus(string path)
    {
        t_filename.Text = path;         
    }

    public void updatePrintStatus()
    {      
        t_printed.Text = "sent to printer";
    }

    public void updateImportStatus(string clientName)
    {
        t_client.Text = clientName;
    }

    public void updateArchiveStatus()
    {
        t_archived.Text = "archived";
    }
}

在没有主窗体的任何Invoke()的情况下调用该代码时:

private void button1_Click(object sender, EventArgs e)
    {
        ImportStatusDialog nDialog = new ImportStatusDialog();

        nDialog.Show();

        nDialog.updateFileStatus("test");
        Thread.Sleep(1000);
        nDialog.updateImportStatus("TestClient");
        Thread.Sleep(1000);
        nDialog.updatePrintStatus();
        Thread.Sleep(1000);
        nDialog.updateArchiveStatus();
        Thread.Sleep(1000);

        nDialog.Close();
    }

即使我这样称呼它:

private void button3_Click(object sender, EventArgs e)
    {
        ImportStatusDialog nDialog = new ImportStatusDialog();

        nDialog.Show();

        if (this.InvokeRequired)
        {
            this.Invoke((MethodInvoker)delegate
            {
                nDialog.updateFileStatus("Test");
            });
        }
        else
        {
            nDialog.updateFileStatus("Test");
        }

        Thread.Sleep(1000);

        if (this.InvokeRequired)
        {
            this.Invoke((MethodInvoker)delegate
            {
                nDialog.updatePrintStatus();
            });
        }
        else
        {
            nDialog.updatePrintStatus();
        }

        Thread.Sleep(1000);

        if (this.InvokeRequired)
        {
            this.Invoke((MethodInvoker)delegate
            {
                nDialog.updateImportStatus("cName");
            });
        }
        else
        {
            nDialog.updateImportStatus("cName");
        }

        Thread.Sleep(1000);

        if (this.InvokeRequired)
        {
            this.Invoke((MethodInvoker)delegate
            {
                nDialog.updateArchiveStatus();
            });
        }
        else
        {
            nDialog.updateArchiveStatus();
        }

        Thread.Sleep(1000);

        nDialog.Close();
    }

在设计器中看起来像这样的对话框(在我的例子中)

designer view

将显示如下:

dialog at runtime

当我使用docs代替ShowDialog()时,对话框会显示,但正如API Doc指出的那样

  

您可以使用此方法在您的中显示模式对话框   应用。调用此方法时,后面的代码不是   执行直到对话框关闭后

这不是我想要的,特别是因为它意味着对话框更新只会在它再次关闭后才会发生。

我在这里做错了什么?这似乎是一个微不足道的问题,但解决方案却逃避了我。请记住,我是C#GUI编程的新手。

另外,我想问一下使用Invoke()的正确位置是什么?在调用对话框方法时,或者在对话框更新方法本身中,您是否会在主窗体中使用它?

1 个答案:

答案 0 :(得分:2)

这看起来不像你在这里使用多个线程,因此不需要调用内容。只有跨线程调用才需要调用 - 您正在创建多个表单,但所有代码都在同一个线程中运行。

如果您正在主UI线程中进行工作(正如按钮单击事件中的代码所暗示的那样),那么只需定期调用Application.DoEvents()以允许刷新进度表单。

更好的解决方案是为您的工作使用BackgroundWorker,并定期报告其进度。

然后,您可以在BackgroundWorker的ProgressChanged事件中更新进度表单(将在主线程中执行,因此您仍然不必调用任何内容)。