【问题标题】:WPF multithreaded progress dialogWPF 多线程进度对话框
【发布时间】:2010-12-25 19:40:50
【问题描述】:

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

公共部分类 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;)。我只是在思考如何设置它时遇到了一些麻烦。

【问题讨论】:

    标签: c# wpf multithreading dialog progress


    【解决方案1】:

    你想错了。

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

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

    在您的工作进行中时,请定期致电BackgroundWorker.ReportProgress。这将在 UI 线程上推动进度。

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

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

    【讨论】:

    • 对,我更新了我的问题以反映这一点(这是正确的)但是,当我尝试在我的 login() 函数中设置变量时出现错误。
    【解决方案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 成员)

    【讨论】:

      【解决方案3】:

      要访问从非 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# 副本。他有一整节关于多线程和后台工作线程。

      【讨论】:

      • 您可以考虑使用if (Dispatcher.CheckAccess()) action(); else Invoke(action);,而不是您的if 语句。不知道为什么 CheckAccess() 是一个隐藏函数(不会出现在 Intellisense 中),但它非常适合这个。请参阅msdn.microsoft.com/en-us/library/… 以获取与您的几乎相同的示例。
      • @Will Eddins - 感谢您的提示,现在代码看起来干净多了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多