ReExCommand 上的CanExecute无法正常工作

我正在使用MVVM Light V3 alpha 3编写一个WPF 4应用程序(使用VS2010 RC),并且在这里遇到了一些奇怪的行为……

我有一个打开一个Window的命令,那个Window创建了ViewModel等等 – 没有什么奇怪的。

在那个Window我有一些RelayCommand ,例如:

 CategoryBeenSelected = new RelayCommand(() => OnCategoryUpdate = true); 

没有什么奇怪的 – 它按照我的预期工作。

问题是我不能使用通用RelayCommand的CanExecute方法/ lambda表达式。

这有效:

 DeleteCategoryCommand = new RelayCommand(DeleteCategory); 

但这不是:

 DeleteCategoryCommand = new RelayCommand(DeleteCategory, CanDeleteCategory); 

窗口没有出现。 我的意思是,我单击打开窗口的按钮,应用程序刚刚被阻止,几秒钟后,Window的InitializeComponent方法抛出NullReferenceException (对象引用未设置为对象的实例)

简而言之,如果我在RelayCommand上放置一个CanExecute方法,那么拥有该ViewModel(带有RelayCommand )的Window无法实例化。 如果我删除了CanExecuteCanExecute显示Window

这里的问题在哪里? 我糊涂了。

谢谢。

编辑:根据要求,这是堆栈跟踪:

 PresentationFramework.dll中出现类型为“System.NullReferenceException”的第一次机会exception
   在GalaSoft.MvvmLight.Command.RelayCommand`1.CanExecute(Object parameter)
   在System.Windows.Controls.Primitives.ButtonBase.UpdateCanExecute()
   在System.Windows.Controls.Primitives.ButtonBase.OnCommandChanged(DependencyObject d,DependencyPropertyChangedEventArgs e)
   在System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
   在System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
   在System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
   在System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex,DependencyProperty dp,PropertyMetadata metadata,EffectiveValueEntry oldEntry,EffectiveValueEntry&newEntry,Boolean coerceWithDeferredReference,Boolean coerceWithCurrentValue,OperationType operationType)
   在System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp,Object value,PropertyMetadata metadata,Boolean coerceWithDeferredReference,Boolean coerceWithCurrentValue,OperationType operationType,Boolean isInternal)
   在System.Windows.DependencyObject.SetValue(DependencyProperty dp,Object value)
   在MS.Internal.Xaml.Runtime.ClrObjectRuntime.SetValue(Object inst,XamlMember属性,Object value)
   在MS.Internal.Xaml.Runtime.PartialTrustTolerantRuntime.SetValue(Object obj,XamlMember属性,Object value)
   在System.Xaml.XamlObjectWriter.Logic_ApplyPropertyValue(ObjectWriterContext ctx,XamlMember prop,Object value,Boolean onParent)
   在System.Xaml.XamlObjectWriter.Logic_DoAssignmentToParentProperty(ObjectWriterContext ctx)
   在System.Xaml.XamlObjectWriter.WriteEndObject()
   在System.Windows.Markup.WpfXamlLoader.TransformNodes(XamlReader xamlReader,XamlObjectWriter xamlWriter,Boolean onlyLoadOneNode,Boolean skipJournaledProperties,Boolean shouldPassLineNumberInfo,IXamlLineInfo xamlLineInfo,IXamlLineInfoConsumer xamlLineInfoConsumer,XamlContextStack`1 stack,IStyleConnector styleConnector)
   在System.Windows.Markup.WpfXamlLoader.Load(XamlReader xamlReader,IXamlObjectWriterFactory writerFactory,Boolean skipJournaledProperties,Object rootObject,XamlObjectWriterSettings settings,Uri baseUri)
   在System.Windows.Markup.WpfXamlLoader.LoadBaml(XamlReader xamlReader,Boolean skipJournaledProperties,Object rootObject,XamlAccessLevel accessLevel,Uri baseUri)
   在System.Windows.Markup.XamlReader.LoadBaml(Stream stream,ParserContext parserContext,Object parent,Boolean closeStream)
   在System.Windows.Application.LoadComponent(对象组件,Uri resourceLocator)
   在c:\ Users \ Jesus \ Documents \ Visual Studio 2010 \ Projects \ ApuntaNotas \ ApuntaNotas \ Views \ CategoryEditorView.xaml:第1行中的ApuntaNotas.Views.CategoryEditorView.InitializeComponent()
   在C:\ Users \ Jesus \ Documents \ Visual Studio 2010 \ Projects \ ApuntaNotas \ ApuntaNotas \ Views \ CategoryEditorView.xaml.cs中的ApuntaNotas.Views.CategoryEditorView..ctor():第18行
 PresentationFramework.dll中出现类型为“System.NullReferenceException”的第一次机会exception

看来RelayCommand会将参数的值转换为genericsT.

但是你不能将null转换为结构,因为exception告诉你!

如果使用可为空的结构初始化RelayCommand,它将按预期工作!

 RelayCommand or RelayCommand> 

HTH

Arcturus在确定问题是什么方面是正确的,但是我不喜欢使用可空原语的解决方案。 除非我有充分的理由使用它们,否则我个人不喜欢可以为空的原语。

相反,我更改了RelayCommand的实现,如下所示:

  bool ICommand.CanExecute(object parameter) { if (parameter == null && typeof(T).IsValueType) { return CanExecute(default(T)); } return CanExecute((T)parameter); } 

我没有对通用的Execute方法进行相同的更改(至少目前为止),因为如果命令确实需要参数,我不认为在这种情况下失败是不合理的。

CanExecute的问题是WPF系统有时会在评估某些绑定之前调用它。 例如:

    

在上面的XAML中,您注意到command参数绑定到控件的实际宽度。 但是,在“imageScrollViewer”控件必须布局/渲染之前,WPF将在按钮的命令上调用CanExecute – 因此没有实际的宽度/高度。 当用户单击按钮并调用Execute时,控件将被布局,以便将值发送到命令。 如果不是 – 我认为失败是应该期待的 – 但只有当用户实际点击按钮时。

当然我不喜欢CanExecute和Execute的不同行为,但是现在它似乎符合框架提出的限制。 我可能会发现一个让我感到悲伤的情景,但我一直都喜欢这个改变。

也许,在这个时候,参数是null