Form.Show()间歇地在线程中显示表单

时间:2010-12-23 01:21:27

标签: c# multithreading

我需要在打开文件时显示进度对话框,这是一项耗时的操作。为此,我在我的打开文件函数中使用了以下内容:

           //some code
           ...
           ...
           ...
           bool done = false;

            //Show progress in a separate thread. 
            System.Threading.ThreadPool.QueueUserWorkItem((x) =>
            {
                using (var progressDialog = new ProgressDialog())
                {
                    progressDialog.TopMost = true;
                    progressDialog.Show();

                    while (!done)
                    {
                        if(progressDialog.Message != this.strProgressMsg)
                            progressDialog.Message = this.strProgressMsg;

                        Application.DoEvents();
                    }

                    progressDialog.Close();
                }
            });

           ....
           ....
           done = true;
           ....
           ....

问题: 进度条对话框显示一些时间,有时则不显示。我的文件打开功能在主线程中运行。有人可以指出我正确的检测,为什么会发生这种情况?

2 个答案:

答案 0 :(得分:5)

你有这种倒退。这是它应该如何工作:

  1. 关闭线程以打开文件,并使用progressDialog.Invoke()回调主线程以执行GUI更新。 (它不应该设置strProgressMsg并等待其他事情注意到更改 - 让它直接将更新发送到对话框。)
  2. 从主线程以模态方式显示进度对话框。
  3. 这样的事情:

    using (var progressDialog = new ProgressDialog()) {
        progressDialog.TopMost = true;
    
        System.Threading.ThreadPool.QueueUserWorkItem((x) =>
        {
            try {
                // this represents whatever loop you use to load the file
                while (...) {
                    // do some work loading the file
    
                    // update the status
                    progressDialog.Invoke(new MethodInvoker(
                        () => progressDialog.Message = "Hello, World!"));
                }
            } finally {
                // done working
                progressDialog.Invoke(new MethodInvoker(progressDialog.Close));
            }
        });
    
        // this will block until the thread closes the dialog
        progressDialog.ShowDialog();
    }
    

答案 1 :(得分:1)

表单需要一条消息才能正常工作。您可以使用Application.RunForm.ShowDialog启动一个消息循环,这两个消息都是阻塞调用,所以很明显这个策略在工作线程中不能很好地工作。您可以通过以下两种方式使其正常工作。

  • 在UI线程上创建progressDialog并通过Control.Invoke向其发送消息,以使用新的进度信息对其进行更新。
  • 在UI线程上创建progressDialog,并使用System.Windows.Forms.Timer轮询变量,以获取工作线程发布的新进度信息。

在这种情况下,轮询方法优于Control.Invoke方法,因为:

  • 它打破了Control.Invoke强加的UI和工作线程之间的紧密耦合。
  • 它将更新UI线程的责任放在它应该属于的UI线程上。
  • UI线程决定更新的发生时间和频率。
  • UI消息泵没有溢出的风险,就像工作线程启动的编组技术一样。
  • 在继续执行下一步之前,工作线程不必等待确认已执行更新(即,您在UI和工作线程上获得更多吞吐量)。