J'utilise le modèle MVVM dans ma première application WPF et j'ai un problème avec quelque chose de basique que je suppose. P>
Lorsque l'utilisateur frappe le bouton "Enregistrer" à mon avis, une commande est exécutée qui appelle la sauvegarde du vide privé () dans mon mode de vue. P>
Le problème est que le code dans "Enregistrer ()" prend un certain temps pour exécuter, alors je voudrais masquer le bouton "Enregistrer" dans la vue UI avant d'exécuter le gros morceau du code. P>
Le problème est que la vue ne met pas à jour jusqu'à ce que tous les codes soient exécutés dans la vue ViewModel. Comment puis-je forcer la vue à redessiner et à traiter les événements de l'état de propriété avant d'exécuter le code Save ()? P>
En outre, je voudrais une manière réutilisable, de sorte que je puisse facilement faire la même chose dans d'autres pages aussi. Quelqu'un d'autre a fait quelque chose comme ça déjà? Un message "Chargement ..."? P>
6 Réponses :
Si cela prend beaucoup de temps, envisagez d'utiliser un fil séparé, par exemple en utilisant un travailleur d'arrière-plan code>, de sorte que le thread de l'interface utilisateur peut rester réactif (c'est-à-dire mettre à jour l'interface utilisateur) pendant que l'opération est effectuée.
Dans votre dans le gestionnaire d'événements code> Dowork code> de votre travailleur d'arrière-plan, vous effectuez une longue opération d'économie. p> dans le gestionnaire code> exécuté code> Ce qui est exécuté dans le fil d'interface utilisateur, vous définissez exemple de code (non testé): p> Enregistrer la méthode code>, vous seriez p>
isbusysocignant code> Boolean qui est lié à votre UI, cache le bouton Enregistrer et affiche peut-être une barre de progression avec
isindétermine = true code> ) et li>
isbusysocignant code> sur false et peut-être modifier d'autres éléments dans l'interface utilisateur pour montrer que vous avez terminé. p>
Désolé je suis un Dumbo total quand il s'agit de enfiler. Dans le code de sauvegarde I (parfois) essayez de naviguer vers une autre page. Mais parce que je suis dans un autre fil, cela donne une erreur d'exécution. Je suppose que je dois faire un rappel au fil d'origine et naviguer de là à l'autre page. Mais je vais essayer cela moi-même, je suis sûr qu'il n'est pas difficile de communiquer avec le fil d'origine.
"Le fil d'appel ne peut pas accéder à cet objet car un fil différent est propriétaire." est le message que je reçois. Si vous savez par cœur ce dont j'ai besoin, faites le moi savoir :-)
RunworkerCompleted, c'est ce dont j'avais besoin. Merci beaucoup. Aussi génial de voir que je peux déclarer l'événementHandler comme ça, ne savait pas ça!
Oui, RunworkerComplet est exactement le bon endroit pour cela puisqu'il fonctionne dans le fil de l'interface utilisateur. Si vous devez modifier UI Stuff pendant i> l'opération de sauvegarde, vous pouvez utiliser appliquer.Current.dispatcher.invoke code> ou le fichier
ReportProgress code> méthode / < Code> ProgressChanged code> Combinaison d'événements du travailleur d'arrière-plan code>.
Vous devez toujours désactiver votre bouton de sauvegarde lorsque vous êtes true == True, ou vous devez fournir une serrure dans la Dowork afin de ne pas vous mettre dans une condition de course.
Est-il suffisant de définir la canexecute sur "isbusyspunaving == False" de mon Savecommand?
Je suppose. Si vous souhaitez faire quelque chose de fantaisie, vous pouvez également ajouter du texte de "économie en cours ..." sur votre interface utilisateur dont la visibilité est liée à isbusyspunaving via un BooleantVisiviConverter code>.
Vous pouvez toujours faire quelque chose comme ceci:
public class SaveDemo : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private bool _canSave; public bool CanSave { get { return _canSave; } set { if (_canSave != value) { _canSave = value; OnChange("CanSave"); } } } public void Save() { _canSave = false; // Do the lengthy operation _canSave = true; } private void OnChange(string p) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(p)); } } }
(1) Si vous définissez _Cansave au lieu de CANSAVE, ONCHANGE ne sera pas soulevé. (2) Je ne pense pas que cela fonctionnerait, car économisez des exécutions dans le fil de l'interface utilisateur, l'interface utilisateur WPF ne sera donc mise à jour avant la fin de l'enregistrement.
Oui, j'apprécie la réponse mais je ne pense pas que cela résoue mon problème. La suggestion de Heinz lui a fixé.
@Heinzi - Bon point sur le CANVERSAVE, et oui, il fonctionnera car le changement de notification est soulevé au début de l'opération Save - par conséquent, les mises à jour de l'UI à ce moment-là.
Vous utilisez le motif MVVM, la commande de votre bouton de sauvegarde est donc définie sur une instance de l'objet routéCommand qui est ajouté à la collection CommandBindings de la fenêtre de manière déclarée ou impérativement.
supposant que vous le faites déclarative. Quelque chose comme p> pour le gestionnaire d'événements routés exécutés, votre méthode de sauvegarde (), à l'entrée, vous définissez une variable sur FALSE, à votre retour, vous le réglez sur TRUE. Quelque chose comme. P> pour le gestionnaire de l'événement routé Canexecute, la méthode Save_Canexecute (), vous utilisez la variable comme l'une des conditions. P> void ShowSelectedXray_CanExecute(object sender, CanExecuteRoutedEventArgs e)
{
e.CanExecute = _canExecute && _others;
}
Vous pouvez y accomplir par le code suivant ..
workerThread.join(); SaveButtton.isEnable = true;
Réponse tardive, mais je pensais que cela serait bien de saisir un peu aussi.
Au lieu de créer votre propre nouveau thread, il serait probablement préférable de laisser tomber jusqu'à la filetage pour exécuter la sauvegarde. Il ne force pas à courir instantanément, comme créer votre propre thread, mais il vous permet de sauvegarder des ressources de filetage. P>
Le moyen de faire est le suivant: P>
ThreadPool.QueueUserWorkItem(Save);
Merci pour votre réponse. J'ai utilisé la réponse acceptée dans ma demande et cela fonctionne bien. Je ne sais pas comment l'utilisation des ressources est par rapport à votre solution, mais le travail d'arrière-plan est très pratique de travailler avec.
Keith: En .NET 3.5 et plus, envisagez des expressions Lambda pour couper l'objet État si vous n'en avez pas besoin. Threadpool.QueueUeUeUserSerWorkItem (état => sauvegarder ()); Peut-être un peu plus de frais généraux ici, mais le code est souvent simplifié avec moins de méthodes d'apaisement des délégués flottant. Vous pouvez également utiliser un Lambda pour lancer l'objet d'état sur tout ce dont vous avez besoin et retirer des propriétés spécifiques. Threadpool.QueueUeUeUserMorworkItem (State => Enregistrer (Etat comme Savaregs) .Length, état comme Savelgs) .Timestamp);