J'ai récemment commencé à programmer dans WPF et suis tombé sur le problème suivant. Je ne comprends pas comment utiliser la méthode Dispatcher.Invoke()
. J'ai de l'expérience dans le threading et j'ai créé quelques programmes Windows Forms simples où je viens d'utiliser le
Control.CheckForIllegalCrossThreadCalls = false;
Oui, je sais que c'est plutôt nul, mais il s'agissait d'applications de surveillance simples.
Le fait est que je suis en train de créer une application WPF qui récupère les données en arrière-plan, je démarre un nouveau thread pour effectuer l'appel permettant de récupérer les données (à partir d'un serveur Web), je souhaite maintenant les afficher sur mon formulaire WPF. Le problème, c’est que je ne peux définir aucun contrôle à partir de ce fil. Pas même une étiquette ou quoi que ce soit. Comment cela peut-il être résolu?
Répondre aux commentaires:
@ Jalfp:
Donc, j'utilise cette méthode Dispatcher dans la «nouvelle bande de roulement» lorsque je reçois les données? Ou devrais-je demander à un travailleur d'arrière-plan de récupérer les données, de les placer dans un champ et de démarrer un nouveau thread qui attend jusqu'à ce que ce champ soit rempli et d'appeler le répartiteur pour afficher les données récupérées dans les contrôles?
La première chose à faire est de comprendre que Dispatcher n’est pas conçu pour exécuter une longue opération de blocage (telle que la récupération de données d’un serveur Web, etc.). Vous pouvez utiliser le répartiteur lorsque vous souhaitez exécuter une opération qui sera exécutée sur le thread d'interface utilisateur (telle que la mise à jour de la valeur d'une barre de progression).
Ce que vous pouvez faire est de récupérer vos données dans un travailleur en arrière-plan et d'utiliser la méthode ReportProgress pour propager les modifications dans le thread d'interface utilisateur.
Si vous avez vraiment besoin d’utiliser le Dispatcher directement, c’est assez simple:
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => this.progressBar.Value = 50));
japf a répondu correctement. Juste au cas où vous envisagez des actions sur plusieurs lignes, vous pouvez écrire comme ci-dessous.
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => {
this.progressBar.Value = 50;
}));
Informations destinées aux autres utilisateurs souhaitant connaître les performances:
Si votre code DOIT être écrit pour des performances élevées, vous pouvez d’abord vérifier si l’appel est requis à l’aide de l’indicateur CheckAccess.
if(Application.Current.Dispatcher.CheckAccess())
{
this.progressBar.Value = 50;
}
else
{
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(() => {
this.progressBar.Value = 50;
}));
}
Notez que la méthode CheckAccess () est masquée dans Visual Studio 2015, alors écrivez-la simplement sans attendre qu'intelliSense l'ait affichée. Notez que CheckAccess a une surcharge sur les performances (surcharge en quelques nanosecondes). Ce n'est que mieux lorsque vous voulez économiser la microseconde nécessaire pour effectuer l'invocation à tout prix. En outre, il est toujours possible de créer deux méthodes (avec invoke, et sans sans) lorsque la méthode appelée est sûre de savoir si elle est ou non dans UI Thread. Ce n'est que très rarement, dans des cas rares, où vous devriez vous pencher sur cet aspect du répartiteur.
Lorsqu'un thread est en cours d'exécution et que vous souhaitez exécuter le thread principal de l'interface utilisateur qui est bloqué par le thread actuel, utilisez ce qui suit:
fil actuel:
Dispatcher.CurrentDispatcher.Invoke(MethodName,
new object[] { parameter1, parameter2 }); // if passing 2 parameters to method.
Fil de l'interface principale:
Application.Current.Dispatcher.BeginInvoke(
DispatcherPriority.Background, new Action(() => MethodName(parameter)));