Canexecute на Relaycommand не работает
-
21-09-2019 - |
Вопрос
Я пишу приложение WPF 4 (с VS2010 RC), используя MVVM Light V3 Alpha 3, и здесь сталкиваюсь с каким -то странным поведением ...
У меня есть команда, которая открывает Window
, И это окно создает ViewModel и так далее - ничего странного там.
В этом Window
у меня есть немного RelayCommand
S, например:
CategoryBeenSelected = new RelayCommand(() => OnCategoryUpdate = true);
Ничего странного снова - это работает, как я ожидал.
Проблема в том, что у меня не может быть экспрессии CANEXCUTE / 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
Решение
Похоже, что RelayCommand будет отдать значение параметра в общий T.
Но вы не можете разыграть NULL в структуру, как говорит вам исключение!
Если вы инициализируете RelayCommand с нулевой структурой, он будет работать так, как и ожидалось!
RelayCommand<int?> or RelayCommand<Nullable<int>>
Hth
Другие советы
Arcturus был прав в определении проблемы, однако мне не нравилось решение использования нулевых примитивов. Лично мне не нравятся нулевые примитивы, если у меня нет очень хорошей причины для их использования.
Вместо этого я изменил реализацию RelayCommand следующим образом:
bool ICommand.CanExecute(object parameter)
{
if (parameter == null && typeof(T).IsValueType)
{
return CanExecute(default(T));
}
return CanExecute((T)parameter);
}
Я не внес такого же изменения для общего метода выполнения (по крайней мере, на данный момент), потому что я не думаю, что в этом случае неразумно терпеть неудачу, если команда действительно ожидает аргумента.
Проблема с 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» обязательно выложен/рендеринг, поэтому фактическая ширина/высота не существует. К тому времени, когда пользователь нажимает кнопку и выполняется выполнение, конечно, элемент управления выложено, чтобы значения отправлялись в команду. Если нет - я думаю, что сбой - это то, что следует ожидать, но только тогда, когда пользователь фактически нажимает кнопку.
Конечно, мне не нравится различное поведение Canexecute и выполнение, но сейчас это, кажется, вписывается в ограничения, представленные в рамках. Я могу найти сценарий, в котором это вызывает у меня горе, но мне понравилось все изменения.
Может, в настоящее время параметр null
?