WPF多线程进度对话框

时间:2009-12-11 20:03:59

标签: c# wpf multithreading dialog progress

更新了这是我遇到的一个有趣的问题。我需要在后台进程运行时显示进度对话框。通常,这可以工作,但问题是我需要在后台进程中设置公共静态数据。以下是我试图完成的一个例子:

public partial class MainWindow : Window
{
    public static Service binding;
    public static Result lr;
    public progressDialog dlg;

    private void login()
    {
        string sPwd = txtPwd.Password;
        string sEmail = txtEmail.Text;
        binding = new Service();
        lr = binding.login(sEmail, sPwd);
    }
    private void btnLogin_Click(object sender, RoutedEventArgs e)
    {
        BackgroundWorker worker = new BackgroundWorker;
        worker.DoWork += new DoWorkEventHandler(worker_DoWork);
        worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted)
        worker.RunWorkerAsync();
        dlg = new progressDialog();
        dlg.Show();
        login();
    }
    private void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        e.Result = login();
    }
    private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        this.Hid();
        Window1 newWindow = new Window1();
        newWindow.Show();
        dlg.Close();
    }

我知道,因为这样,它将无法工作,因为login()是一个void,并且实际上并没有返回一个值,以便在DoWork事件中使用e.Result。但是,我已经设置了一个登录类来传递参数,我仍然收到错误,指出我无法访问UI线程。主要问题是lr和绑定是由另一个窗口访问的,因此它们必须是公共静态数据(从另一个窗口我设置公共静态服务绑定= MainWindow.binding;)。我只是在解决如何设置这个问题时遇到了一些麻烦。

3 个答案:

答案 0 :(得分:4)

你正在以错误的方式思考这个问题。

您需要在主线程上显示进度对话框,而不是在后台工作者中显示。

然后,在BackgroundWorker.DoWork处理程序中,您可以完成实际工作。这在后台线程中运行。

定期,当您的工作进展时,请致电BackgroundWorker.ReportProgress。这将把进度推向UI线程上的偶数。

您需要订阅BW.ProgressChanged事件,您可以在此处更新progressDialog中的进度条。这会自动发生在UI线程上。

您的工作以及对ReportProgress的调用需要成为DoWork事件处理程序中的唯一内容。

答案 1 :(得分:2)

您需要从前台线程上的TextBoxes(txtEmail和txtPwd)中读取,而不是后台线程。这是一个例子:

class MainWindow
{
  private string _email;
  private string _password;

  private void btnLogin_Click(...)
  {
    // running on UI thread here - can touch text boxes
    _email = txtEmail.Text;
    _password = txtPwd.Text;
    // ... set up worker ...
    worker.RunWorkerAsync();
  }

  private void login()
  {
    binding = new Service();
    // running on background thread here
    // but safe to access _email and _password they're just data, not UI controls
    lr = binding.login(_email, _password);
  }
}

编辑:更好的是,将电子邮件和密码作为参数传递,而不是将它们存储在成员变量中:

  private void btnLogin_Click(...)
  {
    // running on UI thread here - can touch text boxes
    LoginInfo loginInfo = new LoginInfo(txtEmail.Text, txtPwd.Text);
    // ... set up worker ...
    worker.RunWorkerAsync(loginInfo);  // note argument
  }

  private void worker_DoWork(...)
  {
    LoginInfo loginInfo = (LoginInfo)(e.Argument);
    // now pass the LoginInfo to login() as an argument
  }

(并删除_email和_password成员)

答案 2 :(得分:1)

要访问在非UI线程中引发的事件调用的事件处理程序中的UI元素,您需要类似以下代码的内容:

Action action = () => FinalUpdate();
if (Thread.CurrentThread != Dispatcher.Thread)
{
    Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, action);
}
else
{
    action();
}

如果可以,请抓住Bill Wagner的More Effective C#副本。他有关于多线程和后台工作者线程的整个部分。