2010-02-21 32 views
11

我正在写使用MVVM光V3阿尔法3 WPF应用程序4(与VS2010 RC)和正在运行到这里的一些怪异的行为...CanExecute上RelayCommand <T>不工作

我打开一个命令Window,并且该窗口创建ViewModel等 - 没有什么奇怪的。再次

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

没什么奇怪的 - 它的工作原理如我所料:

在这种Window我有一些RelayCommand S,例如。

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

这工作:

DeleteCategoryCommand = new RelayCommand<int>(DeleteCategory); 

但这并不:

DeleteCategoryCommand = new RelayCommand<int>(DeleteCategory, CanDeleteCategory); 

窗口不显示。我的意思是,我点击打开窗口的按钮,应用程序就被阻止,几秒钟后,窗口的InitializeComponent方法抛出一个NullReferenceException(对象引用未设置为对象的实例)

总之,如果我把CanExecute方法放在RelayCommand<T>,Window拥有那ViewModel(与RelayCommand<T>)不能实例化。如果我删除CanExecute,则显示Window

问题在哪里?我很困惑。

谢谢。

编辑:按照要求,这里是堆栈跟踪:

 
A first chance exception of type 'System.NullReferenceException' occurred in PresentationFramework.dll 
    at GalaSoft.MvvmLight.Command.RelayCommand`1.CanExecute(Object parameter) 
    at System.Windows.Controls.Primitives.ButtonBase.UpdateCanExecute() 
    at System.Windows.Controls.Primitives.ButtonBase.OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 
    at System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e) 
    at System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e) 
    at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args) 
    at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType) 
    at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal) 
    at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value) 
    at MS.Internal.Xaml.Runtime.ClrObjectRuntime.SetValue(Object inst, XamlMember property, Object value) 
    at MS.Internal.Xaml.Runtime.PartialTrustTolerantRuntime.SetValue(Object obj, XamlMember property, Object value) 
    at System.Xaml.XamlObjectWriter.Logic_ApplyPropertyValue(ObjectWriterContext ctx, XamlMember prop, Object value, Boolean onParent) 
    at System.Xaml.XamlObjectWriter.Logic_DoAssignmentToParentProperty(ObjectWriterContext ctx) 
    at System.Xaml.XamlObjectWriter.WriteEndObject() 
    at System.Windows.Markup.WpfXamlLoader.TransformNodes(XamlReader xamlReader, XamlObjectWriter xamlWriter, Boolean onlyLoadOneNode, Boolean skipJournaledProperties, Boolean shouldPassLineNumberInfo, IXamlLineInfo xamlLineInfo, IXamlLineInfoConsumer xamlLineInfoConsumer, XamlContextStack`1 stack, IStyleConnector styleConnector) 
    at System.Windows.Markup.WpfXamlLoader.Load(XamlReader xamlReader, IXamlObjectWriterFactory writerFactory, Boolean skipJournaledProperties, Object rootObject, XamlObjectWriterSettings settings, Uri baseUri) 
    at System.Windows.Markup.WpfXamlLoader.LoadBaml(XamlReader xamlReader, Boolean skipJournaledProperties, Object rootObject, XamlAccessLevel accessLevel, Uri baseUri) 
    at System.Windows.Markup.XamlReader.LoadBaml(Stream stream, ParserContext parserContext, Object parent, Boolean closeStream) 
    at System.Windows.Application.LoadComponent(Object component, Uri resourceLocator) 
    at ApuntaNotas.Views.CategoryEditorView.InitializeComponent() in c:\Users\Jesus\Documents\Visual Studio 2010\Projects\ApuntaNotas\ApuntaNotas\Views\CategoryEditorView.xaml:line 1 
    at ApuntaNotas.Views.CategoryEditorView..ctor() in C:\Users\Jesus\Documents\Visual Studio 2010\Projects\ApuntaNotas\ApuntaNotas\Views\CategoryEditorView.xaml.cs:line 18 
A first chance exception of type 'System.NullReferenceException' occurred in PresentationFramework.dll 
+1

也许你可以附加一个堆栈跟踪?这可能有助于了解哪里出了问题。 – Vlad

+0

对不起,我忘记了,它是:) –

+0

这很奇怪:反射器告诉函数'CanExecute'是这样定义的:'public bool CanExecute(object parameter){return(this._canExecute == null)| | this._canExecute((T)parameter)); }'。没有什么可以抛出异常。 – Vlad

回答

7

看来,RelayCommand将参数强制转换值一般T.

但是你不能投一个空的结构,作为异常告诉你!

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

RelayCommand<int?> or RelayCommand<Nullable<int>> 

HTH

+0

嗯,这应该是原因...但它有点奇怪..我没有看到任何代码使用可为空...... –

+0

是的,这是正确的..'double'或'int'是值类型,不能为空。如果你让它们为可空类型,它应该工作。将'null'投射到一个结构将产生一个异常!请参阅Vlads评论的方法,您可以将演员视为T! – Arcturus

+0

尝试编译double test =(double)null;在通用世界中,您将获得运行时异常! ;) – Arcturus

0

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

2

大角星是在弄清问题是什么是正确的,但我不喜欢使用可空原语的解决方案。除非我有充分的理由使用它们,否则我个人不喜欢可空的基元。

相反,我改变RelayCommand的执行情况如下:

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

我没有为通用Execute方法(至少目前如此),这同改变,因为我不认为这是不合理的在这种情况下,如果命令真的确实需要一个参数,就会失败。

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

 <Button Content="Fit To Width" Command="{Binding Path=FitToWidthCommand}" CommandParameter="{Binding ElementName=imageScrollViewer, Path=ActualWidth}" /> 
     <Button Content="Fit To Height" Command="{Binding Path=FitToHeightCommand}" CommandParameter="{Binding ElementName=imageScrollViewer, Path=ActualHeight}" /> 

在上面的XAML中,您注意到命令参数绑定到控件的实际宽度。但是,在“imageScrollViewer”控件必须布置/呈现之前,WPF将在按钮的命令中调用CanExecute - 因此没有实际的宽度/高度。当用户点击按钮并调用Execute时,当然控件已经布置好,所以值会被发送到命令中。如果没有 - 我认为失败是应该预期的 - 但只有当用户实际点击按钮时。

当然,我不喜欢CanExecute和Execute的不同行为,但现在它似乎符合框架提出的限制。我可能会发现这种情况导致我悲伤,但我一直喜欢这种变化。

相关问题