CanExecute sur RelayCommand<T> ne fonctionne pas
Je suis en train d'écrire une application WPF 4 (avec VS2010 RC) à l'aide de MVVM Light V3 alpha 3 et je suis en cours d'exécution dans certains comportements étranges ici...
J'ai une commande qui ouvre un Window
, et que la Fenêtre crée le ViewModel et donc sur rien de bizarre là.
Dans ce Window
j'ai quelques RelayCommand
s, par exemple:
CategoryBeenSelected = new RelayCommand(() => OnCategoryUpdate = true);
Rien de bizarre encore - il fonctionne comme je m'y attendais.
Le problème est que je ne peut pas avoir une méthode CanExecute /expression lambda avec un générique RelayCommand.
Cela fonctionne:
DeleteCategoryCommand = new RelayCommand<int>(DeleteCategory);
Mais ce n'est pas:
DeleteCategoryCommand = new RelayCommand<int>(DeleteCategory, CanDeleteCategory);
La Fenêtre ne s'affiche pas. Je veux dire, je clique sur le bouton qui ouvre la fenêtre, et l'application est bloquée et quelques secondes plus tard, La Fenêtre InitializeComponent
méthode lève une NullReferenceException
(la référence d'Objet n'est pas définie à une instance d'un objet)
En bref, Si j'ai mis un CanExecute
Méthode sur un RelayCommand<T>
, le Window
que possède que ViewModel (avec le RelayCommand<T>
) ne peut pas être instanciée. Si je supprime le CanExecute
, le Window
montre.
Où est le problème ici? Je suis confus.
Merci.
EDIT: Comme l'a demandé, voici la trace de la pile:
Une exception de première chance de type 'System.NullReferenceException' s'est produite dans PresentationFramework.dll au GalaSoft.MvvmLight.Commande.RelayCommand`1.CanExecute(paramètre de l'Objet) au Système.De Windows.Les contrôles.Primitives.ButtonBase.UpdateCanExecute() au Système.De Windows.Les contrôles.Primitives.ButtonBase.OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) au Système.De Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e) au Système.De Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e) au Système.De Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args) au Système.De Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata métadonnées, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, operation type. operation type.) au Système.De Windows.DependencyObject.SetValueCommon(DependencyProperty dp, la valeur de l'Objet, PropertyMetadata métadonnées, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, operation type. operation type., Boolean isInternal) au Système.De Windows.DependencyObject.SetValue(DependencyProperty dp, la valeur de l'Objet) au MME Internes.Xaml.Moment de l'exécution.ClrObjectRuntime.SetValue(Objet inst, XamlMember propriété, la valeur de l'Objet) au MME Internes.Xaml.Moment de l'exécution.PartialTrustTolerantRuntime.SetValue(Object obj, XamlMember propriété, la valeur de l'Objet) au Système.Xaml.XamlObjectWriter.Logic_ApplyPropertyValue(ObjectWriterContext ctx, XamlMember prop, la valeur de l'Objet, Boolean onParent) au Système.Xaml.XamlObjectWriter.Logic_DoAssignmentToParentProperty(ObjectWriterContext ctx) au Système.Xaml.XamlObjectWriter.WriteEndObject() au Système.De Windows.Balisage.WpfXamlLoader.TransformNodes(XamlReader xamlReader, XamlObjectWriter xamlWriter, Boolean onlyLoadOneNode, Boolean skipJournaledProperties, Boolean shouldPassLineNumberInfo, IXamlLineInfo xamlLineInfo, IXamlLineInfoConsumer xamlLineInfoConsumer, XamlContextStack`1 pile, IStyleConnector styleConnector) au Système.De Windows.Balisage.WpfXamlLoader.Charge(XamlReader xamlReader, IXamlObjectWriterFactory writerFactory, Boolean skipJournaledProperties, Objet rootObject, XamlObjectWriterSettings paramètres, Uri baseUri) au Système.De Windows.Balisage.WpfXamlLoader.LoadBaml(XamlReader xamlReader, Boolean skipJournaledProperties, Objet rootObject, XamlAccessLevel accessLevel, Uri baseUri) au Système.De Windows.Balisage.XamlReader.LoadBaml(Stream stream, ParserContext parserContext, de l'Objet parent, Boolean closeStream) au Système.De Windows.Application.LoadComponent(composant Objet, Uri resourceLocator) au ApuntaNotas.Les vues.CategoryEditorView.InitializeComponent() dans c:\Users\Jesus\Documents\Visual Studio 2010\Projects\ApuntaNotas\ApuntaNotas\Views\CategoryEditorView.xaml:ligne 1 au ApuntaNotas.Les vues.CategoryEditorView..ctor() dans C:\Users\Jesus\Documents\Visual Studio 2010\Projects\ApuntaNotas\ApuntaNotas\Views\CategoryEditorView.xaml.cs:ligne 18 Une exception de première chance de type 'System.NullReferenceException' s'est produite dans PresentationFramework.dll
Désolé, j'ai oublié que, ça y est 🙂
C'est étrange: Réflecteur dit que la fonction
CanExecute
est définie de telle façon: public bool CanExecute(object parameter) { return (this._canExecute == null) || this._canExecute((T) parameter)); }
. Il n'y a rien à jeter une exception.Peut-être que vous pouvez essayer de reproduire le problème sur un petit exemple?
Aha, j'ai obtenu quelque chose de nouveau. Il fonctionne si vous avez une RelayCommand avec une chaîne ou un objet, si vous utilisez autre chose (int, bool, double..) il meurt. Ce n'est pas pertinent si vous avez réellement envoyer un paramètre ou pas. À propos de l'exemple. Il arrive dans WPF3.5 / Mvvm light 2 et WPF4 / MVVM light 3 alpha3. Que pouvez-vous courir? (Je ne sais pas si vous avez besoin de MVVM light installer ou pas)
OriginalL'auteur Jesus Rodriguez | 2010-02-21
Vous devez vous connecter pour publier un commentaire.
Il semble que la RelayCommand sera jeté la valeur du paramètre générique T.
Mais vous ne pouvez pas convertir une valeur null à une structure, comme l'exception et vous dit!
Si vous initialisez la RelayCommand avec un nullable struct, il fonctionnera comme prévu!
HTH
Oui, c'est correct.. un
double
ouint
sont des types valeur, et ne peut pas être null. Si vous les types nullables, il devrait fonctionner. Castingnull
à une structure produira une exception! Voir Vlads commentaire avec la méthode où vous pouvez voir le casting pour T!essayez de compiler double test = (double)null;.. dans le générique de monde, vous obtiendrez une exception d'exécution! 😉
OriginalL'auteur
Arcturus était correct dans l'identification de ce qui était le problème, mais je n'ai pas comme la solution de l'utilisation de nullable primitives. Personnellement, je n'aime pas les valeurs null primitives, à moins que j'ai une très bonne raison de les utiliser.
Au lieu de cela, j'ai changé la mise en œuvre de RelayCommand comme suit:
Je n'ai pas fait ce même changement pour le générique de la méthode Execute (au moins pour l'instant) parce que je ne pense pas qu'il est déraisonnable de s'échouer dans ce cas, si la commande n'a vraiment s'attendre à un argument.
Le problème avec CanExecute est que le WPF système appelle parfois devant certaines liaisons peuvent être évalués. Par exemple:
Ci-dessus XAML, vous remarquez que la commande de paramètre est lié à la largeur réelle d'un contrôle. Cependant, WPF va appeler CanExecute sur le bouton de commande avant la "imageScrollViewer" le contrôle est nécessairement énoncées/rendu - donc, il n'y a pas de réelle largeur/hauteur. Au moment où l'utilisateur clique sur le bouton et Exécuter est appelée, bien sûr, le contrôle est aménagé de sorte que les valeurs sont envoyés à la commande. Sinon, je pense que l'échec est ce qu'il devrait être - mais seulement lorsque l'utilisateur clique sur le bouton.
Bien sûr, je n'aime pas le comportement différent de CanExecute et de l'Exécuter, mais pour l'instant cela semble correspondre dans les limites présentées par le cadre. Je peut trouver un scénario où cela me cause du chagrin, mais j'ai été au goût du changement jusqu'à présent.
OriginalL'auteur
Peut-être, à ce moment, le paramètre est
null
?OriginalL'auteur