2011-12-22 4 views
1

Je suis au milieu d'une application MVVM. Ici, j'ai une collection (ObservableCollection) qui peut ajouter, supprimer des opérations effectuées à partir d'un thread différent. Voici le code. J'ai essayé de le garder aussi complet que possible:WPF Dispatcher ne fonctionne pas comme BGWorker

private Thread _thread = null; 
private Dispatcher _UIDispatcher = null; 
public ObservableCollection<string> ListOfStrings { get; private set; } 

Le cteur:

public MainWindowViewModel(Dispatcher dispatcher) 
{ 
      this.ListOfStrings = new ObservableCollection<string>(); 
      this.StartAsyncCall = new RelayCommand(AsyncCall, CanCallAsynch); 
      this._UIDispatcher = dispatcher; 
} 

Je prends comme référence le répartiteur de la vue (Il est pas une bonne pratique mais comme il fait des tests d'unité Gary Hall a suggéré une approche intéressante en utilisant AOP, mais je peux le changer plus tard quand même.). Et cette méthode que j'appelle via une commande

private void AsyncCall() 
{ 
    if (this.ListOfStrings.Count > 0) 
     this.ListOfStrings.Clear(); 
    //_backgroundWorker.RunWorkerAsync(); 
    this._thread = new Thread(new ThreadStart(AddNumbersToList)); 
    this._thread.IsBackground = true; 
    this._thread.Start(); 
} 
private void AddNumbersToList() 
{ 
     Action delAddNum = new Action(AddNumbersToList); 
     for (int i = 0; i < 100000000; i++) 
     { 
      if (null != this._UIDispatcher && 
      !this._UIDispatcher.CheckAccess()) 
        _UIDispatcher.Invoke(DispatcherPriority.Render,delAddNum); 
      else 
      { 
        this.ListOfStrings.Add(Convert.ToString(i.ToString())); 
      } 
     } 
    } 

Le ObservableCollection est lié à une zone de liste dans la vue. L'effet que je m'attendais à ce que l'ObservableCollection reçoive de nouveaux numéros, ils devraient être affichés dans l'IU. L'interface utilisateur serait également sensible. Mais étrangement, l'interface utilisateur ne répond pas. La même chose quand j'essaie d'atteindre via un travailleur de fond est finement atteint. Est-ce que quelqu'un peut me dire où je me trompe? Je ne veux pas aller pour le travailleur d'arrière-plan car je veux la flexibilité d'utiliser la classe Thread plus granulaire. est sous le code que j'utilise avec BGWorker et il fonctionne très bien:

private BackgroundWorker _backgroundWorker = new BackgroundWorker(); 

Le CTOR:

public MainWindowViewModel() 
{ 
    this.ListOfStrings = new ObservableCollection<string>(); 
    this.StartAsyncCall = new RelayCommand(AsyncCall, CanCallAsynch); 
    _backgroundWorker.DoWork += AddNumbersToList; 
    _backgroundWorker.RunWorkerCompleted += this.LoadResultsCompleted; 
    _backgroundWorker.WorkerReportsProgress = true; 
    _backgroundWorker.ProgressChanged += new ProgressChangedEventHandler(_backgroundWorker_ProgressChanged); 
} 

Les méthodes:

private void AsyncCall() 
{ 
    if (this.ListOfStrings.Count > 0) 
     this.ListOfStrings.Clear(); 
     this._backgroundWorker.RunWorkerAsync(); 
} 

private void AddNumbersToList(object sender, DoWorkEventArgs e) 
{ 
     BackgroundWorker bw = sender as BackgroundWorker; 
     if(null!=bw) 
     for (int i = 0; i < 10; i++) 
     { 
      bw.ReportProgress(0, i); 
        Thread.Sleep(1000); 
     } 
} 


private void LoadResultsCompleted(object sender, RunWorkerCompletedEventArgs e) 
{ 
    if (e.Error == null) 
    { 
      this.ListOfStrings.Add("Completed"); 
      CommandManager.InvalidateRequerySuggested(); 
    } 
} 

//This works just wonderful: 
void _backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    this.ListOfStrings.Add(Convert.ToString(e.UserState.ToString())); 
} 

Nous vous remercions à l'avance.

..James

+0

ne pouvait pas le fait que vous ajoutez 100000000 éléments dans votre approche tracée vs 10 avec le BackgroundWorker ont quelque chose à voir avec ça? – madd0

+0

Je ne pense pas. Vous pouvez remplacer le code du module de thread par celui du module BGWorker. Le résultat reste toujours le même. – James

+0

Il m'est arrivé de lire quelque part que Dispatcher ne fournit pas de fonctionnalité de multithreading exactement. Mais je ne suis pas très clair à ce sujet. J'essaie de l'utiliser dans WPF comme j'ai utilisé "Invoke" en winforms. Mais alors, quelle devrait être mon approche à cela? – James

Répondre

0

Son récursivité qui est accroché votre code ...

Cela fonctionne pour moi ...

private void AsyncCall() 
    { 
     if (this.MyCollection.Count > 0) 
      this.MyCollection.Clear(); 

     this._thread = new Thread(new ThreadStart(AddNumbersToList)); 
     this._thread.IsBackground = true; 

     this._thread.Start(); 
    } 

    private void AddNumbersToList() 
    { 
     for (int i = 0; i < 100000000; i++) 
     { 
      this.Dispatcher.Invoke(
       new Action(
        delegate 
         { 
          this.MyCollection.Add(Convert.ToString(i.ToString())); 
         })); 

      Thread.Sleep(100); 
     } 
    } 
+0

Obtenez-vous les numéros apparaissant un par un dans votre liste (ou n'importe quel contrôle)? Cela n'a pas fonctionné pour moi !! :(Je vais revérifier de toute façon Bien que je ne pouvais pas obtenir une réponse au problème ci-dessus, j'ai eu un travail autour d'un ObservableList personnalisé [ici] (http://blog.quantumbitdesigns.com/2008/07/22/ wpf-cross-thread-collection-liaison-partie-4-the-grand-solution /) .La liste présentée à cet article a fonctionné ok pour moi.Je vais essayer de vérifier son code – James

+0

oui, cela montre les numéros apparaissant un par 1 –

+0

Merci, vous avez raison, l'appel récursif de la même méthode est bloqué, il se peut que je doive approfondir le modèle Threading dans WPF.Le Dispatcher.Invoke fonctionne certainement différemment de Control.Invoke() dans Winforms. – James