WPF 多线程进度对话框
Posted
技术标签:
【中文标题】WPF 多线程进度对话框【英文标题】:WPF multithreaded progress dialog 【发布时间】: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;)。我只是在思考如何设置它时遇到了一些麻烦。
【问题讨论】:
【参考方案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 - 感谢您的提示,现在代码看起来干净多了。以上是关于WPF 多线程进度对话框的主要内容,如果未能解决你的问题,请参考以下文章