可以在 RelayCommand上执行不工作

发布于 2024-08-22 04:26:44 字数 4251 浏览 2 评论 0原文

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

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

简而言之,如果我将 CanExecute 方法放在 RelayCommand 上,则拥有的 Window ViewModel(使用 RelayCommand)无法实例化。如果我删除 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

I'm writing a WPF 4 app (with VS2010 RC) using MVVM Light V3 alpha 3 and am running into some weird behaviour here...

I have a command that opens a Window, and that Window creates the ViewModel and so on - nothing weird there.

In that Window I have some RelayCommands, for example:

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

Nothing weird again - it works as I expected.

The problem is that I cannot have a CanExecute method / lambda expression with a generic RelayCommand.

This works:

DeleteCategoryCommand = new RelayCommand<int>(DeleteCategory);

But this does not:

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

The Window doesn'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 method throws a NullReferenceException (Object reference not set to an instance of an object)

In short, If I put a CanExecute Method on a RelayCommand<T>, the Window that owns that ViewModel (with the RelayCommand<T>) can't be instantiated. If I remove the CanExecute, the Window shows up.

Where is the problem here? I'm confused.

Thank you.

EDIT: As requested, here is the stack trace:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

以可爱出名 2024-08-29 04:26:45

看起来 RelayCommand 会将参数的值转换为通用 T。

但是您不能将 null 转换为结构,正如异常告诉您的那样!

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

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

华泰

It seems that the RelayCommand will cast the value the parameter to the generic T.

But you cannot cast a null to a struct, as the exception tells you!

If you initialize the RelayCommand with a nullable struct, it will work as expected!

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

HTH

顾北清歌寒 2024-08-29 04:26:45

Arcturus 正确地确定了问题所在,但我不喜欢使用可为空基元的解决方案。我个人不喜欢可为 null 的原语,除非我有充分的理由使用它们。

相反,我更改了 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 将在“imageScrollViewer”控件必须布局/渲染之前对按钮的命令调用 CanExecute - 因此没有实际的宽度/高度。当用户单击按钮并调用“执行”时,当然会布置控件,以便将值发送到命令。如果不是 - 我认为失败是应该预料到的 - 但只有当用户实际单击按钮时。

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

Arcturus was correct in identifying what the problem was, however I didn't like the solution of using nullable primitives. I personally don't like nullable primitives unless I have a very good reason to use them.

Instead, I changed the implementation of RelayCommand as follows:

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

I didn't make this same change for the generic Execute method (at least for now) because I don't think it is unreasonable to fail in that case if the command really does expect an argument.

The problem with CanExecute is that the WPF system will sometimes call it before certain bindings can be evaluated. For example:

        <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}" />

In the above XAML, you notice the command parameter is bound to the actual width of a control. However, WPF will call CanExecute on the button's command before the "imageScrollViewer" control is necessarily laid out/rendered - so there is no actual width/height. By the time the user clicks the button and Execute is invoked, of course the control is laid out so values get sent to the command. If not - I think failing is what should be expected - but only when the user actually clicks the button.

Of course I don't like the different behavior of CanExecute and Execute, but for now it seems to fit within the restrictions presented by the framework. I may find a scenario where this causes me grief, but I've been liking the change so far.

苦笑流年记忆 2024-08-29 04:26:45

聚会太晚了,但我一直在摸索这个问题,问题是我导入了错误的命名空间。

我应该导入:

using GalaSoft.MvvmLight.CommandWpf;

但我导入了:

using GalaSoft.MvvmLight.Command;

希望这对某人有帮助!

Incredibly late to the party but I've just been scratching my head over this and the issue was I'd imported the wrong namespace.

I should have imported:

using GalaSoft.MvvmLight.CommandWpf;

but I had imported:

using GalaSoft.MvvmLight.Command;.

Hope this helps someone!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文