2010-04-21 3 views
0

J'ai écrit une application dans WPF/VB et séparé la logique métier et l'interface utilisateur dans différents projets. La couche de gestion utilise un port série qui s'exécute sur un thread différent. Maintenant que j'essaie d'écrire une interface de ligne de commande pour la même couche de gestion, elle semble échouer lorsque .Invoke() est appelée. (pas d'erreur, ne fonctionne pas)Dispatcher.CheckAccess() ne fonctionne pas à partir de mon application de console, est-il un meilleur moyen

Je suis assez sûr que la raison pour laquelle j'ai dû ajouter checkaccess et .invoke était parce que j'ai des collections qui seraient changées pendant le traitement des données de port série et voulaient que NotifyCollectionChanged être géré par la liaison de données WPF. (La raison pour laquelle je ne suis pas sûr à 100% est que j'ai écrit cette partie il y a quelques mois et tout a bien fonctionné depuis l'interface graphique, maintenant l'application console m'a fait repenser)

Je voudrais ma couche de gestion pour exécuter ces processus sur le thread ils ont été créés, j'ai besoin de cela à la fois de ma version de l'interface graphique et la version de ligne de commande. Est-ce que j'utilise mal Dispatcher dans ma couche de gestion? Existe-t-il un meilleur moyen de gérer un événement à partir du port série, puis de revenir au thread principal pour traiter les données?

Mise à jour:

Private Delegate Sub NewDataRecieved(ByVal byteBuffer() As Byte) 
Private Sub DataReceived(ByVal byteBuffer() As Byte) Handles _serial.DataRecieved 
    If _dispatcher.CheckAccess() Then 
     ProcessTheData 
    Else 
     Dim dataReceivedDelegate As NewDataRecieved 
     dataReceivedDelegate = New NewDataRecieved(AddressOf DataReceived) 
     _dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, dataReceivedDelegate, byteBuffer) 
    End If 
End Sub 
+0

Si ne pas utiliser WPF approprié, envisagez d'utiliser SynchronizationContext, une approche plus bas niveau pour offrir la possibilité d'envoyer en toute sécurité cross-fil. –

Répondre

2

Invoke ne fait rien parce que vous ne disposez pas d'un répartiteur cours d'exécution, pour obtenir les services d'un répartiteur, vous devez appeler Dispatcher.Run (http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.run.aspx)

Maintenant, votre problème est que l'appel Dispatcher. Exécuter fera en sorte que WPF prenne le contrôle du thread - et ce n'est probablement pas ce que vous voulez faire dans une application console.

Je pense que la meilleure option dans votre situation est de supprimer le code de synchronisation de thread (tout ce qui parle à un répartiteur) de votre couche de gestion et dans les objets wrapper.

L'application WPF peut continuer à fonctionner comme auparavant en utilisant les objets wrapper et l'application console peut utiliser directement la couche de gestion "brute".

Mise à jour: voici un exemple de l'enveloppe ci-dessous, vous devez créer une méthode/propriété pour chaque membre public de la classe d'origine qui fait le travail de thread et appelle l'objet original.

public class BOWrapper : INotifyPropertyChanged 
{ 
    private BO _bo; 
    private Dispather _dispather; 

    public BOWrapper(BO bo, Dispatcher dispather) 
    { 
     _bo = bo; 
     _dispather = dispather; 
     _bo.PropertyChanged += BOPropertyChanged; 
    } 

    public string SomeValue 
    { 
     get { return _bo.SomeValue; } 
    } 

    private void BOPropertyChanged(object sender, PropertyChangedEventArgs ea) 
    { 
     _dispatcher.Invoke(
      new Action<PropertyChangedEventArgs>(
       e=> 
       { 
        var handler = PropertyChanged; 
        if(handler!=null) handler(this,e); 
       }),ea); 
    } 
} 

La classe d'emballage est 100% code boilerplate et vous pouvez probablement utiliser un générateur de code pour créer, peut-être même utiliser quelque chose comme DynamicProxy (http://www.castleproject.org/dynamicproxy/index.html) pour générer automatiquement lors de l'exécution.

+0

J'aime votre explication quant à pourquoi invoke ne fonctionne pas. J'essaie de comprendre ce que vous entendez par "objets wrapper" Si je crée encore une autre couche qui doit être conservée, cela supprime en quelque sorte tout bénéfice de partager la couche métier entre deux applications. De plus, je ne suis pas tout à fait sûr de la façon dont je créerais ce wrapper pour gérer les événements de la couche de gestion et ensuite déclencher plus d'événements sur le bon thread sans pratiquement doubler la quantité de code. – zimmer62

+0

J'ai mis à jour la réponse, j'espère que cela répond à votre question – Nir

+0

+1. On dirait que cela a bien été répondu! –

0

WPF dans une application console? Dans les applications de la console, il n'y a aucune restriction sur l'exécution de la fonction dans un thread spécifique. Vous pouvez gérer n'importe quel événement dans le contexte du thread appelant, en fournissant une synchronisation si nécessaire.

+0

Le problème est que la couche de gestion est un projet distinct et je veux qu'il fonctionne à la fois pour une application WPF et une application console qui fait référence à ce projet ... Non WPF dans une application console. J'ai deux applications, et une couche de gestion. Les hooks que j'ai ajoutés dans la couche de gestion pour résoudre un problème avec WPF empêchent l'application de la console de fonctionner. – zimmer62

0

Une alternative: Dans le thread où vous voulez un Dispatcher, utilisez la propriété statique Dispatcher.CurrentDispatcher. Si un répartiteur n'a pas encore été créé pour le thread, il est créé automatiquement (si possible). Vous pouvez stocker cette valeur quelque part pour permettre aux autres threads d'utiliser le répartiteur.

Dans le code:

public class BusinessLayerThread 
{ 
    public BusinessLayerThread() 
    { 
     Dispatcher = Dispatcher.CurrentDispatcher; 
    } 

    public static Dispatcher Dispatcher { get; private set; } 
} 
+0

C'est en fait ce que je faisais, et ça ne marche pas. _dispatcher.CheckAccess() renvoie la valeur false et le code supposé s'exécuter lorsque _dispatcher.Invoke (System.Windows.Threading.DispatcherPriority.Normal, dataReceivedDelegate, byteBuffer) est appelé ne se produit jamais. – zimmer62

+0

Essayez d'utiliser des points d'arrêt où le répartiteur est stocké et où l'appel est terminé. Ensuite, vérifiez qu'il s'agit bien de deux threads différents. –

Questions connexes