首页 文章

ReExCommand <T>上的CanExecute无法正常工作

提问于
浏览
11

我正在使用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<int>(DeleteCategory);

但这不是:

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

Window没有't show up. I mean, I click the button that opens the window, and the app just gets blocked and some seconds later, The Window' s InitializeComponent 方法抛出 NullReferenceException (对象引用未设置为对象的实例)

简而言之,如果我在 RelayCommand<T> 上放置一个 CanExecute 方法,那么拥有该ViewModel(带有 RelayCommand<T> )的 Window 就无法实例化 . 如果我删除了 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

3 回答

  • 0

    看来RelayCommand会将参数的值转换为泛型T.

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

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

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

    HTH

  • 2

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

    相反,我更改了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中,您注意到command参数绑定到控件的实际宽度 . 但是,在“imageScrollViewer”控件必须布局/渲染之前,WPF将在按钮的命令上调用CanExecute - 因此没有实际的宽度/高度 . 当用户单击按钮并调用Execute时,控件将被布局,以便将值发送到命令 . 如果不是 - 我认为失败是应该期待的 - 但仅当用户实际点击按钮时 .

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

  • 7

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

相关问题