Подтвердить что ты не робот

CanExecute on RelayCommand <T> не работает

Я пишу приложение WPF 4 (с VS2010 RC), используя MVVM Light V3 alpha 3 и нахожусь в каком-то странном поведении здесь...

У меня есть команда, которая открывает Window, и это окно создает ViewModel и т.д. - там ничего странного.

В этом Window у меня есть несколько RelayCommand s, например:

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

Ничего странного - он работает так, как я ожидал.

Проблема заключается в том, что я не могу использовать метод CanExecute/lambda с общим релейным сообщением.

Это работает:

DeleteCategoryCommand = new RelayCommand<int>(DeleteCategory);

Но это не так:

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

Окно не отображается. Я имею в виду, что я нажимаю кнопку, которая открывает окно, и приложение просто блокируется, а через несколько секунд метод Window InitializeComponent выдает NullReferenceException (ссылка объекта не установлена ​​на экземпляр объекта)

Короче говоря, если я положил метод CanExecute на a RelayCommand<T>, то Window, которому принадлежит эта ViewModel (с RelayCommand<T>), не может быть создан. Если я удалю CanExecute, появится Window.

Где проблема? Я в замешательстве.

Спасибо.

EDIT: в соответствии с запросом, это трассировка стека:

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
4b9b3361

Ответ 1

Кажется, что RelayCommand передаст значение параметру в общий T.

Но вы не можете передать null в структуру, как это описано в исключении!

Если вы инициализируете RelayCommand с помощью NULL-структуры, она будет работать как ожидалось!

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

НТН

Ответ 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 вы заметите, что параметр команды привязан к фактической ширине элемента управления. Однако WPF будет вызывать CanExecute в команде кнопки до того, как элемент управления "imageScrollViewer" обязательно будет выложен/отображен - поэтому фактической ширины/высоты нет. Когда пользователь нажимает кнопку и вызывается Execute, конечно, элемент управления выставляется так, что значения передаются команде. Если нет - я думаю, что неудача - это то, что следует ожидать - но только тогда, когда пользователь на самом деле нажимает кнопку.

Конечно, мне не нравится различное поведение CanExecute и Execute, но на данный момент оно, похоже, соответствует ограничениям, представленным каркасом. Я могу найти сценарий, когда это вызывает у меня печаль, но до сих пор мне нравилось изменение.

Ответ 3

Возможно, в это время параметр null?

Ответ 4

Невероятно поздно на вечеринку, но я только что почесал голову над этим, и проблема заключалась в том, что я импортировал неправильное пространство имен.

Я должен был импортировать:

using GalaSoft.MvvmLight.CommandWpf;

но я импортировал:

using GalaSoft.MvvmLight.Command;.

Надеюсь, это поможет кому-то!