Le gestionnaire de commandes.InvalidateRequerySuggested() n'est pas assez rapide. Que puis-je faire?
Version Courte
Appels à CommandManager.InvalidateRequerySuggested()
prendre plus de temps pour prendre effet que je voudrais (1 à 2 secondes avant de l'INTERFACE utilisateur commandes sont désactivées).
Version Longue
J'ai un système où je soumettre des tâches à un arrière-plan-fil tâche sur la base de processeur. Cette soumettre arrive sur le WPF thread d'INTERFACE utilisateur.
Lorsque cette soumettre arrive, l'objet qui gère mon thread d'arrière-plan fait deux choses:
-
Il soulève un "occupé" de l'événement (toujours sur le thread de l'INTERFACE utilisateur) que plusieurs modèles de vue de répondre à; lorsqu'ils reçoivent cet événement, ils ont mis un
IsEnabled
drapeau sur eux-mêmes pourfalse
. Les contrôles dans mes vues, qui sont liées aux données à cette propriété, sont immédiatement grisé, qui est ce à quoi je m'attends. -
Il informe mon WPF
ICommand
des objets qu'ils ne devraient pas être autorisés à s'exécuter (encore une fois, toujours sur le thread d'INTERFACE utilisateur). Car il n'y a rien commeINotifyPropertyChanged
pourICommand
objets, je suis obligé d'appelerCommandManager.InvalidateRequerySuggested()
à force de WPF à reconsidérer l'ensemble de mes objets de commande'CanExecute
états (oui, j'ai effectivement besoin pour ce faire: dans le cas contraire, aucun de ces contrôles deviennent handicapés). Contrairement à l'article 1, si, il prend beaucoup plus de temps pour mes boutons/menu articles/etc qui sont à l'aide deICommand
objets à modifier visuellement à un état désactivé que pour les contrôles de l'INTERFACE utilisateur qui ont leurIsEnabled
bien définir manuellement.
Le problème est, à partir d'un UX point de vue, cela ressemble terrible; la moitié de mes contrôles sont immédiatement grisé (parce que leur IsEnabled
propriété est définie sur false), et puis plein de 1-2 secondes plus tard, l'autre moitié de mes contrôles suivre (parce que leur CanExecute
méthodes sont ré-évalués).
Donc, la partie 1 de ma question:
Comme cela peut paraître idiot de se demander s'il existe un moyen pour moi de faire CommandManager.InvalidateRequerySuggested()
faire de l'emploi la plus rapide? Je soupçonne qu'il n'y en a pas.
Assez juste, la partie 2 de ma question:
Comment puis-je contourner cela? Je préfère tous mes contrôles de être désactivé en même temps. Il semble juste un manque de professionnalisme et maladroit autrement. Des idées? 🙂
Vous devez vous connecter pour publier un commentaire.
Cette solution est une version réduite de la solution proposée par Tomáš Kafka(merci à Tomas pour décrire sa solution dans le détail)dans ce fil.
Dans Tomas de la solution qu'il avait
1) DelegateCommand
2) CommandManagerHelper
3) DelegateCommandExtensions
4) NotifyPropertyChangedBaseExtensions
5) INotifyPropertyChangedWithRaise
6) ThreadTools
Cette solution a
1) DelegateCommand
2) DelegateCommandExtensions méthode et NotifyPropertyChangedBaseExtensions méthode de Déléguer le Commandement lui-même.
Note Depuis notre application wpf suit le pattern MVVM et nous nous occupons de commandes au dernier niveau qui s'exécute dans le thread de l'INTERFACE utilisateur nous n'avons pas besoin d'obtenir la référence de l'INTERFACE utilisateur disptacher.
Explication de la solution:
Normalement, quand on la lier à un élément de l'INTERFACE utilisateur(Bouton)de la ICommand de mise en œuvre de la WPF Bouton de registres pour un Événement "CanExecuteChanged" ICommand mise en œuvre .Si votre Icommand mise en œuvre pour "CanExecuteChanged" crochet pour le Gestionnaire de commandes de RequesySuggest événement(lire cet article http://joshsmithonwpf.wordpress.com/2008/06/17/allowing-commandmanager-to-query-your-icommand-objects/) puis quand jamais Gestionnaire de commandes détecte les conditions susceptibles de modifier la capacité d'une commande à exécuter(changements, comme le Focus se déplace et certains événements de clavier) , Gestionnaire de commandes de RequerySuggested événement se produit qui à son tour provoque le Bouton e délégué à être appelé, car nous avons accroché le buttos de delgate de Gestionnaire de commandes de RequerySuggested dans la mise en œuvre de "CanExecuteChanged" dans notre DelegateCommand .
Mais le problème est que ComandManager n'est pas toujours en mesure de détecter les changements. Donc la solution pour augmenter "CanExecuteChanged" lors de notre commande de mise en œuvre(DelegateCommand) détecte un changement.Normalement, lorsque nous déclarons la delagate pour ICommand de CanExecute dans notre viewmodel nous lier à des propriétés déclarées dans notre viewmodel et notre ICommand mise en œuvre peut écouter "propertychanged" événements sur ces propriétés. C'est ce que le "ListenForNotificationFrom" méthode de la DelegateCommand n'. Dans le cas où le code du client n'enregistre pas les propriétés spécifiques des changements DelegateCommand par défaut à l'écoute de tout changement de propriété sur le modèle de vue où la commande est déclarée et définie.
"ControlEvent" dans DelegateCommand qui est liste du Gestionnaire d'événements qui stocke le Bouton
"CanExecuteChange EventHandler" est déclaré comme référence faible pour éviter les fuites de mémoire.
Comment ViewModel utiliser cette DelegateCommand
Il y a 2 façons de l'utiliser.
(le deuxième usage est plus spécifique pour les propriétés que vous souhaitez que la Commande écouter.
Détaillée ViewModel
}
Tomas a une solution sympa, mais pls note il y a un sérieux problème en ce que le CanExecute ne sera pas toujours le feu quand il est lié à un Bouton, pour cette raison :
L' 'null', paramètre passé dans les causes des problèmes avec l'CanExecuteChangedEventManager (utilisé par le WPF Touche de classe à écouter, à des changements sur toutes les commandes qui lui sont liées). Plus précisément, le CanExecuteChangedEventManager maintient une collection de la faiblesse des événements qui doivent être invoquées afin de déterminer si la commande Peut-Execute (), mais cette collection est saisi par l '"expéditeur".
La solution est simple et fonctionne pour moi - modifier la signature de
Désolé, je n'ai pas décrit trop bien, mais un peu dans le rush de rattraper mon dev maintenant, après avoir pris quelques heures pour comprendre cela !
Je suggère de regarder dans ReactiveUI et spécifiquement à la ICommand mise en œuvre qu'il fournit, ReactiveCommand. Il utilise une approche différente de celle DelegateCommand/RelayCommand qui sont mis en œuvre avec les délégués pour CanExecute qui doit être activement évalué. ReactiveCommand de la valeur pour CanExecute est poussé à l'aide de IObservables.
Oui, il y a moyen de le faire fonctionner plus vite!
Command
à garder /cacheCanExecuteState
dans une variable booléenne.RaiseCanExecuteChanged
méthode pour recalculerCanExecuteState
et si il a vraiment changé pour éleverCanExecuteChanged
événement.CanExecute
méthode pour simplement retournerCanExecuteState
.InvalidateRequerySuggested
méthode est appeléeCommand
les abonnés pourront seulement lireCanExecuteState
variable en invoquantCanExecute
méthode et de vérifier si ça a changé ou pas. C'est près de zéro frais généraux. Tous lesCommands
sera désactivé /activé presque dans le même temps.RaiseCanExecuteChanged
méthode qui sera appelée qu'une seule fois pour uneCommand
et seulement pour un nombre limité deCommands
.Essayez d'écrire votre propre liaison qui appelle votre RaiseCanExecuteChanged() dans les convertit? il est plus facile
Juste pour clarifier:
CanExecute
quandCommand property changed
Command property
et appelle ensuiteRaiseCanExecuteChanged()
CommandParameter
A fonctionné pour moi.