38 votes

Veiller OnPropertyChanged() est appelée sur le thread de l'INTERFACE utilisateur en application WPF, MVVM

Dans une application WPF, que je suis en train d'écrire en utilisant le pattern MVVM, j'ai un processus d'arrière-plan qui fait son effet, mais ont besoin pour obtenir des mises à jour de statut de sortir de l'INTERFACE utilisateur.

Je suis en utilisant le pattern MVVM, donc mon ViewModel ne connaît pratiquement rien de la vue (UI) qui est de présenter le modèle de l'utilisateur.

Dire que j'ai la méthode suivante dans mon ViewModel:

public void backgroundWorker_ReportProgress(object sender, ReportProgressArgs e)
{
    this.Messages.Add(e.Message);
    OnPropertyChanged("Messages");
}

De mon point de vue, j'ai une ListBox lié à la propriété Messages (un List<string>) du ViewModel. OnPropertyChanged remplit le rôle de l' INotifyPropertyChanged interface en appelant un PropertyChangedEventHandler.

Je dois m'assurer que OnPropertyChanged est appelée sur le thread d'INTERFACE utilisateur - comment puis-je faire cela? J'ai essayé ce qui suit:

public Dispatcher Dispatcher { get; set; }
public MyViewModel()
{ 
    this.Dispatcher = Dispatcher.CurrentDispatcher;
}

Puis d'ajouter les points suivants à l' OnPropertyChanged méthode:

if (this.Dispatcher != Dispatcher.CurrentDispatcher)
{
    this.Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(delegate
    {
        OnPropertyChanged(propertyName);
    }));
    return;
}

mais cela ne fonctionne pas. Des idées?

36voto

Kent Boogaart Points 97432

WPF regroupe automatiquement les modifications de propriété dans le thread de l'INTERFACE utilisateur. Cependant, il n'est pas le maréchal de collecte des changements, donc je soupçonne votre ajout d'un message d'origine de la panne.

Vous pouvez regrouper les ajouter manuellement vous-même (voir l'exemple ci-dessous), ou utiliser quelque chose comme cette technique j'ai blogué à propos d'un certain temps.

Manuellement les gares de:

public void backgroundWorker_ReportProgress(object sender, ReportProgressArgs e)
{
    Dispatcher.Invoke(new Action<string>(AddMessage), e.Message);
    OnPropertyChanged("Messages");
}

private void AddMessage(string message)
{
    Dispatcher.VerifyAccess();
    Messages.Add(message);
}

8voto

Mike Graham Points 85

J'aime VRAIMENT de Jeremy réponse: Envoi En Silverlight

Résumé:

  • Placer Répartiteur dans le ViewModel semble peu élégante

  • La création d'une Action<Action> propriété, il suffit d'exécuter l'action dans la VM constructeur

  • Lors de l'utilisation de la machine virtuelle à partir de la V, définissez la propriété d'Action pour appeler l'expéditeur

3voto

Jeffrey Knight Points 2654

J'avais un scénario similaire juste cette semaine MVVM (ici aussi). J'ai eu une classe distincte faisant sa chose, un rapport d'état sur un gestionnaire d'événement. Le gestionnaire d'événement est appelé comme prévu, et j'ai pu voir les résultats de revenir à droite sur le temps de Débogage.WriteLine du.

Mais avec WPF, peu importe ce que j'ai fait, l'INTERFACE utilisateur ne serait pas de mise à jour jusqu'à ce que le processus a été complété. Dès que le processus est terminé, l'INTERFACE utilisateur sera mis à jour comme prévu. C'était comme si elle devenait PropertyChanged, mais attend que le thread au complet avant de faire les mises à jour de l'INTERFACE utilisateur à la fois.

(À mon grand désarroi, le même code dans Windows.Les formes avec un DoEvents et .Refresh() a fonctionné comme un charme.)

Jusqu'à présent, j'ai résolu ce problème en démarrant le processus de sa propre thread:

//hook up event handler
myProcess.MyEvent += new EventHandler<MyEventArgs>(MyEventHandler); 

//start it on a thread ...
ThreadStart threadStart = new ThreadStart(myProcess.Start);

Thread thread = new Thread(threadStart);

thread.Start();

et puis dans le gestionnaire d'événements:

private void MyEventHandler(object sender, MyEventArgs e) { 
....
Application.Current.Dispatcher.Invoke(
    			DispatcherPriority.Send,
    			(DispatcherOperationCallback)(arg =>
    			{ 
         //do UI updating here ...
        }), null);

Je ne recommande pas ce code, puisque je suis encore à essayer de comprendre le WPF modèle de thread, comment Dispatcher fonctionne, et pourquoi dans mon cas, l'INTERFACE utilisateur ne serait pas de mise à jour jusqu'à ce que le processus est terminé, même avec le gestionnaire d'événement appelé comme prévu (à dessein?). Mais ce qui a fonctionné pour moi jusqu'à présent.

J'ai trouvé ces deux liens utiles:

http://www.nbdtech.com/blog/archive/2007/08/01/Passing-Wpf-Objects-Between-Threads-With-Source-Code.aspx

http://srtsolutions.com/blogs/mikewoelmer/archive/2009/04/17/dealing-with-unhandled-exceptions-in-wpf.aspx

0voto

Luke Puplett Points 4971

Je me charge du BackgroundWorker.ReportProgress événement hors de ma vue, de modèle et de passer la réelle BackgroundWorker instance et le ViewModel dans ma classe qui définit la asynch méthode(s).

Le asynch méthode appelle ensuite bgWorker.ReportProgress et passe une classe qui encapsule un délégué comme le userstate (comme objet). Le délégué j'écris comme une méthode anonyme.

Dans le gestionnaire d'événements, je lance à partir de l'objet de l'emballage type, puis invoque le délégué à l'intérieur.

Tout cela signifie que je peux le code de l'INTERFACE utilisateur des changements directement à partir du code qui s'exécute de façon asynchrone, mais il a juste ce enroulant autour d'elle.

C'est ce qui explique plus en détail:

http://lukepuplett.blogspot.com/2009/05/updating-ui-from-asynchronous-ops.html

0voto

Jodrell Points 14205

Il s'agit plus d'une extension de la accepté de répondre, mais je l'ai fait avec mon événement virage ...

using System.Threading;

private void Handler(object sender, RoutedEventArgs e)
{
    if (Thread.CurrentThread == this.Dispatcher.Thread)
    {
        //do stuff to this
    }
    else
    {
        this.Dispatcher.Invoke(
            new Action<object, RoutedEventArgs>(Handler),
            sender,
            e);
    }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X