简体   繁体   English

将menuitem IsEnabled绑定到ICommand CanExecute时出现ArgumentNullException

[英]ArgumentNullException when binding menuitem IsEnabled to ICommand CanExecute

I'm trying to disable a menuitem depending on objects in an ObservableCollection. 我试图根据ObservableCollection中的对象禁用menuitem。

MainViewModel: MainViewModel:

public ObservableCollection<ThumbnailModel> Thumbnails { get; set; }

public MainWindowViewModel()
{
    Thumbnails = new ObservableCollection<ThumbnailModel>();
    this.CreateMenu();
}

private void CreateMenu()
{
    //TODO: Add tooltip to menu with short description

    var items = new List<MenuItemViewModel>();

    var item = new MenuItemViewModel();
    item.MenuText = "File";

    item.MenuItems = new List<MenuItemViewModel> { 
        new MenuItemViewModel { MenuText = "Select all", MenuCommand = this.SelectAllCommand, IsEnabled = SelectAllCommand.CanExecute(Thumbnails) },
        new MenuItemViewModel { MenuText = "Unselect all", MenuCommand = this.UnselectAllCommand, IsEnabled = true },
    };

    items.Add(item);

    //And so on
    MenuItems = items;
}

public ICommand SelectAllCommand
{
    get
    {
        return this.selectAllCommand ??
            (this.selectAllCommand = new DelegateCommand(SelectAll, ((t) => ((ObservableCollection<ThumbnailModel>)t).Any(o => !o.IsChecked))));
    }
}

Xaml: XAML:

<Window.Resources>
    <!--Menu template-->
    <HierarchicalDataTemplate DataType="{x:Type viewModels:MenuItemViewModel}"
                              ItemsSource="{Binding Path=MenuItems}">
        <HierarchicalDataTemplate.ItemContainerStyle>
            <Style TargetType="MenuItem">
                <Setter Property="Command"
                        Value="{Binding MenuCommand}"/>
                <Setter Property="CommandParameter" 
                        Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
                <Setter Property="IsEnabled"
                        Value="{Binding IsEnabled}"/>
            </Style>
        </HierarchicalDataTemplate.ItemContainerStyle>
        <StackPanel Orientation="Horizontal">
            <Image Source="{Binding MenuIcon}" />
            <TextBlock Text="{Binding MenuText}" />
        </StackPanel>
    </HierarchicalDataTemplate>
</Window.Resources>


<Menu DockPanel.Dock="Top" ItemsSource="{Binding Path=MenuItems}" />

When opening the File-menu, I get an exception. 打开文件菜单时,出现异常。

System.ArgumentNullException was unhandled HResult=-2147467261 未处理System.ArgumentNullException HResult = -2147467261
Message=Value cannot be null. Message = Value不能为null。 Parameter name: source 参数名称:来源
Source=System.Core 来源= System.Core程序
ParamName=source PARAMNAME =源
StackTrace: at System.Linq.Enumerable.Any[TSource](IEnumerable 1 source, Func 2 predicate) at KoenHoefman.PhotoResizer.ViewModels.MainWindowViewModel.b__e(Object t) in d:\\000 TFS Workspace\\KoenHoefman.PhotoResizer\\Main\\KoenHoefman.PhotoResizer\\ViewModels\\MainWindowViewModel.cs:line 126 at KoenHoefman.PhotoResizer.ViewModels.DelegateCommand.CanExecute(Object parameter) in d:\\000 TFS Workspace\\KoenHoefman.PhotoResizer\\Main\\KoenHoefman.PhotoResizer\\ViewModels\\DelegateCommand.cs:line 95 at MS.Internal.Commands.CommandHelpers.CanExecuteCommandSource(ICommandSource commandSource) at System.Windows.Controls.MenuItem.UpdateCanExecute() at System.Windows.Controls.MenuItem.HookCommand(ICommand command) ... StackTrace:位于System.Linq.Enumerable.Any [TSource](IEnumerable 1 source, Func 2谓词),位于d:\\ 000 TFS Workspace \\ KoenHoefman.PhotoResizer \\ Main \\中的KoenHoefman.PhotoResizer.ViewModels.MainWindowViewModel.b__e(Object t)。 KoenHoefman.PhotoResizer \\ ViewModels \\ MainWindowViewModel.cs:第126行,位于d:\\ 000 TFS Workspace \\ KoenHoefman.PhotoResizer \\ Main \\ KoenHoefman.PhotoResizer \\ ViewModels \\ DelegateCommand.koenHoefman.PhotoResizer.ViewModels.DelegateCommand.CanExecute(Object parameter)第95行在MS.Internal.Commands.CommandHelpers.CanExecuteCommandSource(ICommandSource commandSource)在System.Windows.Controls.MenuItem.UpdateCanExecute()在System.Windows.Controls.MenuItem.HookCommand(ICommand命令)...

At first I tought the reason was the fact that there are no items in MenuItems at the start. 起初,我想知道的原因是这样的事实,即开始时MenuItems中没有任何项目。 However, when I run the folowing code after my menu-creation it returns false (as expected). 但是,当我在菜单创建之后运行以下代码时,它返回false(如预期的那样)。

var y = SelectAllCommand.CanExecute(Thumbnails);

Any idea what's going wrong here and of course how to fix it? 知道这里出了什么问题,当然要怎么解决?

UPDATE Must have looked over it before but when the CanExecute-method is hit, the parameter is null, although I've specified it to be Thumbnails ? UPDATE之前一定已经查看过它,但是当单击CanExecute方法时,该参数为null,尽管我已将其指定为Thumbnails

DelegateCommand: DelegateCommand:

using System;
using System.Windows.Input;

public class DelegateCommand : ICommand
{

    private readonly Action<object> execute;
    private readonly Predicate<object> canExecute;

    public DelegateCommand(Action<object> execute)
        : this(execute, null)
    {}

    public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
    {
        if (execute == null)
        {
            throw new ArgumentNullException("execute");
        }

        this.execute = execute;
        this.canExecute = canExecute;
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void Execute(object parameter)
    {
        this.execute(parameter);
    }

    public bool CanExecute(object parameter) // parameter is null when breakpoint is hit
    {
        return this.canExecute == null || this.canExecute(parameter);
    }
}

If I understand predicates correctly (which is not sure), the method will be executed every time it's called. 如果我正确理解谓词(不确定),则该方法将在每次调用时执行。 But what about the parameter I've put in at the time of the assignment? 但是我在分配时输入的参数呢? Is this only used one time? 这仅使用一次吗?

The definition of a Predicate is this: 谓词的定义是这样的:

public delegate bool Predicate<in T>( T obj)

All it does is some type of compare or test on the obj and returns true or false. 它所做的只是在obj上进行某种比较或测试,并返回true或false。 We see this all the time in LINQ. 我们一直在LINQ中看到这一点。

 var myList = getEmployees();
 var filter = myList.Where(p=>p.lastName == "Jones");

The delegate is the "p" or parameter, and the comparison part is the predicate or bool value.. Notice that the type passed in in "implied" this is because linq has a static class "Where" extension method allowing up to pass in any collection type which will take a predicate as the parm. 委托是“ p”或参数,而比较部分是谓词或bool值。请注意,在“隐含”中传入的类型是因为linq具有静态类“ Where”扩展方法,允许向上传入任何将谓词作为参数的集合类型。 Like this: 像这样:

public static IEnumerable<TSource> Where<TSource>(
    this IEnumerable<TSource> source,
    Func<TSource, int, bool> predicate
)

Per the example at MSFT site on the Delegate command we see the new creation of one, and the second parm is passing a method (pointer) named "CanSubmit" to be called when needed. 在MSFT站点上的Delegate命令上的示例中,我们看到了一个新创建的示例,第二个参数正在传递名为“ CanSubmit”的方法(指针),以在需要时调用。

public MyClass()
{
   this.submitCommand = new DelegateCommand<int?>(this.Submit, this.CanSubmit);
}

private bool CanSubmit(int? customerId)
{
  return (customerId.HasValue && customers.Contains(customerId.Value));
}

Finally figured it out while going through the code, step by step and stumbling upon this question 最终在遍历代码的过程中逐步弄清楚了这个问题

Turns out that 原来是

By default menu items become disabled when its command cannot be executed (CanExecute = false). 默认情况下,菜单项在无法执行命令时被禁用(CanExecute = false)。

(Could not find any reference to that in MSDN ??) (在MSDN中找不到对此的任何引用?)

So the solution became a lot simpler since I didn't need the IsEnabled property on my MenuItemViewModel anymore. 因此,解决方案变得更加简单,因为我不再需要MenuItemViewModel上的IsEnabled属性。

My XAML looks now like: 我的XAML现在看起来像:

<Window.Resources>
    <!--Menu template-->
    <HierarchicalDataTemplate DataType="{x:Type viewModels:MenuItemViewModel}"
                              ItemsSource="{Binding Path=MenuItems}">
        <HierarchicalDataTemplate.ItemContainerStyle>
            <Style TargetType="MenuItem">
                <Setter Property="Command"
                        Value="{Binding MenuCommand}"/>
                <Setter Property="CommandParameter" 
                        Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
                <!-- No longer needed. By default menu items become disabled when its command cannot be executed (CanExecute = false).
                <Setter Property="IsEnabled"
                        Value="{Binding IsEnabled}"/>-->
            </Style>
        </HierarchicalDataTemplate.ItemContainerStyle>
        <StackPanel Orientation="Horizontal">
            <Image Source="{Binding MenuIcon}" />
            <TextBlock Text="{Binding MenuText}" />
        </StackPanel>
    </HierarchicalDataTemplate>
</Window.Resources>

And my commands: 而我的命令:

    public ICommand SelectAllCommand
    {
        get
        {
            return this.selectAllCommand ?? (this.selectAllCommand = new DelegateCommand(SelectAll, delegate(object obj) { return Thumbnails.Any(t => !t.IsChecked); }));
        }
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM