WPF multithreaded progress dialog
Updated This is kind of an interesting problem I am experiencing. I need to have a progress dialog show when a background process is running. Normally, this would work but the problem is that I need to set public static data within the background process. Here is an example of what I am attempting to accomplish:
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)开发者_如何学JAVA 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(); }
I know that as this stands, it will not work because login() is a void and does not actually return a value to use with e.Result in the DoWork event. However, I have set up a login class to pass the parameters to and I still receive errors stating that I cannot access the UI thread. The main problem is that lr and binding are accessed by another window so they must be public static data (from the other window I set public static Service binding = MainWindow.binding;). I'm just having a bit of trouble wrapping my head around how exactly to set this up.
You're thinking about this the wrong way.
You need to show your progress dialog on your main thread, no in your background worker.
Then, in the BackgroundWorker.DoWork handler, you do your actual work. This runs in the background thread.
On regular intervals, while your work is progressing, call BackgroundWorker.ReportProgress. This will push the progress into an even on the UI thread.
You'll want to subscribe to the BW.ProgressChanged event, and here is where you'll update the progress bar in your progressDialog. This happens on the UI thread automatically.
Your work, and the calls to ReportProgress, need to be the only things in the DoWork event handler.
You need to read from the TextBoxes (txtEmail and txtPwd) on the foreground thread, not the background thread. Here's an example:
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);
}
}
EDIT: Even better, pass the email and password as arguments rather than storing them in member variables:
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
}
(and remove the _email and _password members)
To access UI elements in event handlers called from events raised in a non-UI thread you need something like the following code:
Action action = () => FinalUpdate();
if (Thread.CurrentThread != Dispatcher.Thread)
{
Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, action);
}
else
{
action();
}
If you can, get hold of a copy of More Effective C# by Bill Wagner. He has a whole section on multithreading and the background worker thread.
精彩评论