更新了这是我遇到的一个有趣的问题。我需要在后台进程运行时显示进度对话框。通常,这可以工作,但问题是我需要在后台进程中设置公共静态数据。以下是我试图完成的一个例子:
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;)。我只是在解决如何设置这个问题时遇到了一些麻烦。
答案 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#副本。他有关于多线程和后台工作者线程的整个部分。