2010-02-22 3 views
2

J'applique le modèle MVVM. J'ai un bouton qui, lorsqu'on clique dessus, appelle une commande déléguée dans mon ViewModel. Au tout début de cette méthode de délégué, j'ai défini une valeur de propriété (WaitOn) qui doit informer l'utilisateur dans l'interface utilisateur pour qu'il attende en affichant un contrôle animé. Cependant, la liaison pour afficher ce contrôle animé n'est pas actualisée jusqu'à ce que le délégué ait terminé son exécution, moment auquel l'attente est terminée. Pourquoi cela arrive-t-il et que dois-je faire pour le contourner?Pourquoi l'actualisation de liaison est-elle retardée jusqu'à l'achèvement de la commande de délégué? (MVVM)

Exemple XAML:

<Button Command="{Binding DoStuffCommand}" /> 
<ctl:MyAnimatedControl Name="ctlWait" Caption="Please Wait..." 
Visibility="{Binding WaitNotification}" /> 

Snippets de ViewModel:

public bool WaitPart1On 
{ 
    get { return _waitPart1On; } 
    set 
    { 
    _waitPart1On = value; 
    if (_waitPart1On == true) 
    { 
     WaitNotification = "Visible"; 
    } 
    else 
    { 
     WaitNotification = "Hidden"; 
    } 
    RaisePropertyChanged("WaitPart1On"); 
    } 
} 

public string WaitNotification 
{ 
    get { return _waitNotification; } 
    set 
    { 
    _waitNotification = value; 
    RaisePropertyChanged("WaitNotification"); 
    } 
} 


public void DoStuff() 
{ 
    WaitPart1On = true; 
    //Do lots of stuff (really, this is PART 1) 
    //Notify the UI in the calling application that we're finished PART 1 
    if (OnFinishedPart1 != null) 
    { 
    OnFinishedPart1(this, new ThingEventArgs(NewThing, args)); 
    } 
    WaitPart1On = false; 
} 

Et maintenant, code-behind de la XAML pour attraper l'événement déclenché:

public void Part1FinishedEventHandler(NewThing newThing, ThingEventArgs e) 
    { 
     //at this point I expected the WaitPart1On to be set to false 
     //I planned to put a new wait message up (WaitPart2) 
     FinishPart2(); 
    } 

Répondre

4

J'ai eu le travail de solution de Rory, avec un peu de peaufinage. J'ai fini par faire ceci:

public void DoStuff() 
{ 
    WaitPart1On = true; 
    Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, (Action)(() => 

    { 
     //Do lots of stuff 
     WaitPart1On = false; 
    }); 
} 

aide « (action) » pour créer une nouvelle action pour envelopper le code que je voulais dire ne pas avoir à déclarer un délégué partout, et en utilisant la priorité de « Contexte » a permis l'interface utilisateur sa chance de mettre à jour.

+1

Je suis content que vous ayez réussi. Je me demande simplement si le casting de 'Action' est strictement nécessaire, je suis sûr que la syntaxe lambda aurait pris soin de cela pour vous ... – Rory

6

Les chances sont que la liaison est en cours de mise à jour, mais parce que vous faites "beaucoup de choses" sur le thread de l'interface utilisateur, l'application n'a pas l'occasion de mettre à jour l'écran. Vous devriez envisager de déplacer le traitement vers un thread d'arrière-plan ou d'utiliser Dispatcher.BeginInvoke() afin que l'interface utilisateur soit libre de mettre à jour et d'afficher votre message d'attente.

Dans WPF, la classe Dispatcher possède un CurrentDispatcher property statique que vous pouvez utiliser dans ViewModel pour planifier des tâches. Votre méthode DoStuff ressemblerait à quelque chose comme ceci:

public void DoStuff() 
{ 
    WaitOn = true; 
    Dispatcher.CurrentDispatcher.BeginInvoke(() => 
    { 
     //Do lots of stuff 
     WaitOn = false; 
    }); 
} 

Dans Silverlight, vous pouvez accéder au répartiteur de courant en utilisant la classe Deployment:

public void DoStuff() 
{ 
    WaitOn = true; 
    Deployment.Current.Dispatcher.BeginInvoke(() => 
    { 
     //Do lots of stuff 
     WaitOn = false; 
    }); 
} 

Sur une note de côté, vous voudrez peut-être utiliser la BooleanToVisibilityConverter afin que vous ayez uniquement besoin de la propriété OnWait pour la liaison. En outre, votre setter OnWait tire actuellement des notifications même si la propriété est située à la même valeur, vous devez mettre en œuvre comme ceci:

public bool WaitOn 
{ 
    get { return _waitOn; } 
    set 
    { 
    if(value != _waitOn) 
    { 
     _waitOn = value; 
     RaisePropertyChanged("WaitOn"); 
    } 
    } 
} 
+0

Merci pour l'info sur le convertisseur. Je vais certainement faire usage de cela. Mais pour le bit Dispatcher, ce n'est pas si simple depuis l'intérieur du ViewModel. La méthode de mon ViewModel est invoquée à partir d'une liaison de commande directe dans l'interface utilisateur, donc je ne peux pas contrôler les choses tant que la méthode n'est pas déjà en cours. J'ai regardé cet article intéressant sur l'utilisation de Dispatcher avec MVVM -> http://www.wintellect.com/CS/blogs/jlikness/archive/2009/12/16/dispatching-in-silverlight.aspx I Je ne suis pas sûr qu'il va mettre à jour l'interface utilisateur avant qu'elle ne se termine non plus, car elle engloberait tout le contenu de ma méthode aussi. –

+0

@ml_black: J'ai mis à jour ma réponse avec quelques exemples d'utilisation du Dispatcher à partir du modèle View.Votre méthode 'DoStuff' est toujours appelée par le thread UI, et' WaitOn' est défini avant le début du travail, mais tout le reste est appelé via le répartiteur. Je sais que cette méthode fonctionne parce que je l'ai déjà utilisée, mais si cela ne fonctionne pas pour vous, vous pouvez le faire avec des threads créés manuellement ou avec le ThreadPool. Vous devez juste faire attention à la définition de la propriété 'WaitOn' (et de toutes les autres que l'interface utilisateur pourrait être liée) à partir des threads d'arrière-plan. J'espère que cela aide. – Rory

+0

Merci. J'explorais votre suggestion, mais je n'avais pas vu le code écrit aussi clairement. J'ai cela fonctionnel, cependant, ça ne marche pas. Je ne sais pas si cela est peut-être dû au fait que le «tas de choses» que je transmets au Dispatcher déclenchera également un événement que le View gère dans le code-behind. Je vais mettre à jour l'exemple de code un peu. –

Questions connexes