2010-04-05 5 views
25

J'ai rencontré un problème d'accès Croix-Thread incorrect, mais j'ai effectué quelques recherches et j'ai réussi à le résoudre en utilisant Dispatcher.Présentation du répartiteur Silverlight

Maintenant dans mon application, j'ai des objets avec chargement paresseux. Je ferais un appel asynchrone en utilisant WCF et comme d'habitude j'utilise le Dispatcher pour mettre à jour mes objets DataContext, mais cela n'a pas fonctionné pour ce scénario. J'ai cependant trouvé une solution here. Voici ce que je ne comprends pas.

Dans mon UserControl j'ai le code pour appeler une méthode Toggle sur mon objet. L'appel à cette méthode est dans un Dispatcher comme ça.

Dispatcher.BeginInvoke(() => _CurrentPin.ToggleInfoPanel()); 

Comme je l'ai mentionné auparavant, cela ne suffisait pas à satisfaire Silverlight. J'ai dû faire un autre appel Dispatcher dans mon objet. Mon objet est PAS un UIElement, mais une classe simple qui gère tout son propre chargement/sauvegarde.

Ainsi, le problème a été résolu en appelant

Deployment.Current.Dispatcher.BeginInvoke(() => dataContext.Detail = detail); 

au sein de ma classe.

Pourquoi ai-je dû appeler deux fois le Dispatcher pour y parvenir? Un appel de haut niveau ne devrait-il pas suffire? Y a-t-il une différence entre le Deployment.Current.Dispatcher et le Dispatcher dans un UIElement?

+1

Je ne pense pas que vous avez fourni assez de code. Je soupçonne que quelque chose que vous faites lance un appel réseau, qui revient sur un fil de fond, d'où les deux invoque? –

Répondre

21

Idéalement, stockez une seule instance de Dispatcher que vous pouvez utiliser ailleurs sans avoir à vérifier le thread.

L'appel de n'importe quelle instance .Current singleton peut en fait entraîner une vérification d'accès de thread-invocation. En le stockant en premier, vous pouvez éviter cela pour obtenir l'instance partagée.

J'utilise un "SmartDispatcher" qui utilise un répartiteur lorsqu'il est appelé hors-thread, et appelle simplement autrement. Cela résout ce genre de problème.

Poste: http://www.jeff.wilcox.name/2010/04/propertychangedbase-crossthread/

code:

// (c) Copyright Microsoft Corporation. 
// This source is subject to the Microsoft Public License (Ms-PL). 
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details. 
// All other rights reserved. 

using System.ComponentModel; 

namespace System.Windows.Threading 
{ 
    /// <summary> 
    /// A smart dispatcher system for routing actions to the user interface 
    /// thread. 
    /// </summary> 
    public static class SmartDispatcher 
    { 
     /// <summary> 
     /// A single Dispatcher instance to marshall actions to the user 
     /// interface thread. 
     /// </summary> 
     private static Dispatcher _instance; 

     /// <summary> 
     /// Backing field for a value indicating whether this is a design-time 
     /// environment. 
     /// </summary> 
     private static bool? _designer; 

     /// <summary> 
     /// Requires an instance and attempts to find a Dispatcher if one has 
     /// not yet been set. 
     /// </summary> 
     private static void RequireInstance() 
     { 
      if (_designer == null) 
      { 
       _designer = DesignerProperties.IsInDesignTool; 
      } 

      // Design-time is more of a no-op, won't be able to resolve the 
      // dispatcher if it isn't already set in these situations. 
      if (_designer == true) 
      { 
       return; 
      } 

      // Attempt to use the RootVisual of the plugin to retrieve a 
      // dispatcher instance. This call will only succeed if the current 
      // thread is the UI thread. 
      try 
      { 
       _instance = Application.Current.RootVisual.Dispatcher; 
      } 
      catch (Exception e) 
      { 
       throw new InvalidOperationException("The first time SmartDispatcher is used must be from a user interface thread. Consider having the application call Initialize, with or without an instance.", e); 
      } 

      if (_instance == null) 
      { 
       throw new InvalidOperationException("Unable to find a suitable Dispatcher instance."); 
      } 
     } 

     /// <summary> 
     /// Initializes the SmartDispatcher system, attempting to use the 
     /// RootVisual of the plugin to retrieve a Dispatcher instance. 
     /// </summary> 
     public static void Initialize() 
     { 
      if (_instance == null) 
      { 
       RequireInstance(); 
      } 
     } 

     /// <summary> 
     /// Initializes the SmartDispatcher system with the dispatcher 
     /// instance. 
     /// </summary> 
     /// <param name="dispatcher">The dispatcher instance.</param> 
     public static void Initialize(Dispatcher dispatcher) 
     { 
      if (dispatcher == null) 
      { 
       throw new ArgumentNullException("dispatcher"); 
      } 

      _instance = dispatcher; 

      if (_designer == null) 
      { 
       _designer = DesignerProperties.IsInDesignTool; 
      } 
     } 

     /// <summary> 
     /// 
     /// </summary> 
     /// <returns></returns> 
     public static bool CheckAccess() 
     { 
      if (_instance == null) 
      { 
       RequireInstance(); 
      } 

      return _instance.CheckAccess(); 
     } 

     /// <summary> 
     /// Executes the specified delegate asynchronously on the user interface 
     /// thread. If the current thread is the user interface thread, the 
     /// dispatcher if not used and the operation happens immediately. 
     /// </summary> 
     /// <param name="a">A delegate to a method that takes no arguments and 
     /// does not return a value, which is either pushed onto the Dispatcher 
     /// event queue or immediately run, depending on the current thread.</param> 
     public static void BeginInvoke(Action a) 
     { 
      if (_instance == null) 
      { 
       RequireInstance(); 
      } 

      // If the current thread is the user interface thread, skip the 
      // dispatcher and directly invoke the Action. 
      if (_instance.CheckAccess() || _designer == true) 
      { 
       a(); 
      } 
      else 
      { 
       _instance.BeginInvoke(a); 
      } 
     } 
    } 
} 
+0

Des trucs super, merci! – Matt

+1

Ceci est vraiment très bien et corrigé ma migrane. – slashp

6

Si vous utilisez le MVVM light toolkit vous pouvez utiliser la classe DispatcherHelper dans l'espace de noms Galasoft.MvvmLight.Threading dans le dll Extras. Il vérifie l'accès et utilise une propriété statique pour le répartiteur, similaire à SmartDispatcher.

Dans votre cas démarrage App.xaml.cs appel:

DispatcherHelper.Initialize(); 

Alors où vous devez utiliser un répartiteur utilisation:

DispatcherHelper.CheckBeginInvokeOnUI(() => // do stuff;); 
+1

Le lien est incorrect. Le voici: http://mvvmlight.codeplex.com/ – paulwhit

+0

J'ai corrigé le lien, merci. – Aligned

Questions connexes