2009-12-11 6 views
2

Mise à jour Voici un problème intéressant que je rencontre. Je dois avoir une boîte de dialogue de progression quand un processus d'arrière-plan est en cours d'exécution. Normalement, cela fonctionnerait mais le problème est que j'ai besoin de définir des données statiques publiques dans le processus d'arrière-plan. Voici un exemple de ce que je tente d'accomplir:Boîte de dialogue de progression multithread WPF

 
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(); 
    } 

Je sais que cela signifie, il ne fonctionnera pas parce que la connexion() est un vide et ne retourne pas vraiment une valeur à utiliser avec e.Result dans l'événement DoWork. Cependant, j'ai mis en place une classe de connexion pour passer les paramètres et je reçois toujours des erreurs indiquant que je ne peux pas accéder au thread de l'interface utilisateur. Le problème principal est que lr et la liaison sont accédés par une autre fenêtre donc ils doivent être des données statiques publiques (à partir de l'autre fenêtre je mets le service public static binding = MainWindow.binding;). J'ai juste un peu de mal à comprendre comment régler ça.

Répondre

2

Vous devez lire les TextBox (txtEmail et txtPwd) sur le thread de premier plan, pas le thread d'arrière-plan. Voici un exemple:

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: Mieux encore, passer l'e-mail et mot de passe comme arguments plutôt que de les stocker dans des variables membres:

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 
    } 

(et retirer le _email et les membres _password)

+0

Merci. Je pense que je l'ai fouetté. – Joe

4

Vous pensez à ce sujet dans le mauvais sens.

Vous devez afficher votre boîte de dialogue de progression sur votre thread principal, non dans votre assistant d'arrière-plan. Puis, dans le gestionnaire BackgroundWorker.DoWork, vous effectuez votre travail actuel. Cela s'exécute dans le fil d'arrière-plan.

À intervalles réguliers, pendant que votre travail progresse, appelez BackgroundWorker.ReportProgress. Cela va pousser la progression dans un thread pair sur l'interface utilisateur.

Vous voudrez vous abonner à l'événement BW.ProgressChanged, et c'est ici que vous mettrez à jour la barre de progression dans votre ProgressDialog. Cela se produit sur le thread de l'interface utilisateur automatiquement.

Votre travail et les appels à ReportProgress doivent être les seuls éléments du gestionnaire d'événements DoWork.

+0

A droite, j'ai mis à jour ma question pour refléter cela (et c'est correct) cependant, l'erreur survient lorsque je tente de définir les variables dans ma fonction login(). – Joe

1

Pour accéder à des éléments de l'interface utilisateur dans les gestionnaires d'événements appelés d'événements déclenchés dans un thread non-UI vous besoin de quelque chose comme le code suivant:

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

Si vous le pouvez, mettre la main sur une copie de More Effective C# par Bill Wagner . Il a une section entière sur le multithreading et le fil de travail d'arrière-plan.

+1

Au lieu de votre instruction 'if', vous pouvez utiliser l'action' if (Dispatcher.CheckAccess())(); sinon Invoquer (action); Aucune idée pourquoi CheckAccess() est une fonction cachée (n'apparaît pas dans Intellisense), mais c'est parfait pour cela. Voir http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.checkaccess.aspx pour un exemple presque identique au vôtre. –

+0

@Will Eddins - merci pour ce conseil, le code semble beaucoup plus propre maintenant. – ChrisF

Questions connexes