2

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

公共部分类 MainWindow : 窗口
{
    公共静态服务绑定;
    公共静态结果 lr;
    公共进度对话框 dlg;

    私人无效登录()
    {
        字符串 sPwd = txtPwd.密码;
        字符串 sEmail = txtEmail.Text;
        绑定=新服务();
        lr = binding.login(sEmail, sPwd);
    }
    私人无效btnLogin_Click(对象发送者,RoutedEventArgs e)
    {
        BackgroundWorker 工作者 = 新的 BackgroundWorker;
        worker.DoWork += new DoWorkEventHandler(worker_DoWork);
        worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted)
        worker.RunWorkerAsync();
        dlg = 新进度对话框();
        dlg.Show();
        登录();
    }
    private void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        e.Result = login();
    }
    私人无效工人_RunWorkerCompleted(对象发送者,RunWorkerCompletedEventArgs e)
    {
        this.Hid();
        窗口 1 新窗口 = 新窗口 1();
        新窗口.Show();
        dlg.关闭();
    }

我知道,就目前而言,它不会起作用,因为 login() 是一个 void 并且实际上并没有返回一个值以在 DoWork 事件中与 e.Result 一起使用。但是,我已经设置了一个登录类来传递参数,但我仍然收到错误消息,指出我无法访问 UI 线程。主要问题是 lr 和 binding 被另一个窗口访问,所以它们必须是公共静态数据(从另一个窗口我设置 public static Service binding = MainWindow.binding;)。我只是在思考如何设置它时遇到了一些麻烦。

4

3 回答 3

4

你在想这个错误的方式。

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

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

在您的工作进行期间,定期调用BackgroundWorker.ReportProgress。这将推动 UI 线程上的进度。

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

您的工作以及对 ReportProgress 的调用必须是 DoWork 事件处理程序中的唯一内容。

于 2009-12-11T20:12:04.617 回答
2

您需要读取前台线程上的文本框(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 成员)

于 2009-12-11T20:39:50.180 回答
1

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

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

如果可以,请获取Bill Wagner的《更有效的 C# 》的副本。他有一整节关于多线程和后台工作线程。

于 2009-12-11T20:14:22.147 回答