将menuitem IsEnabled绑定到ICommand CanExecute时的ArgumentNullException

时间:2014-11-11 19:49:09

标签: c# wpf mvvm commandbinding

我正在尝试根据ObservableCollection中的对象禁用菜单项。

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:

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

打开文件菜单时,我得到一个例外。

  

System.ArgumentNullException未处理HResult = -2147467261
  Message = Value不能为null。参数名称:来源
  来源= System.Core程序
  PARAMNAME =源
  堆栈跟踪:          在System.Linq.Enumerable.Any [TSource](IEnumerable 1 source, Func 2谓词)          at KoenHoefman.PhotoResizer.ViewModels.MainWindowViewModel.b__e(Object   t)在d:\ 000 TFS中   工作区\ KoenHoefman.PhotoResizer \ MAIN \ KoenHoefman.PhotoResizer \的ViewModels \ MainWindowViewModel.cs:行   126          at KoenHoefman.PhotoResizer.ViewModels.DelegateCommand.CanExecute(Object   参数)在d:\ 000 TFS中   工作区\ KoenHoefman.PhotoResizer \ MAIN \ KoenHoefman.PhotoResizer \的ViewModels \ DelegateCommand.cs:行   95          在MS.Internal.Commands.CommandHelpers.CanExecuteCommandSource(ICommandSource)   commandSource)          在System.Windows.Controls.MenuItem.UpdateCanExecute()          在System.Windows.Controls.MenuItem.HookCommand(ICommand命令)          ...

起初我认为原因是MenuItems中没有项目。 但是,当我在菜单创建后运行以下代码时,它返回false(如预期的那样)。

var y = SelectAllCommand.CanExecute(Thumbnails);

知道这里出了什么问题,当然还有如何修复它?

更新 之前必须查看它,但是当命中CanExecute方法时,参数为null,尽管我已将其指定为Thumbnails

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);
    }
}

如果我正确理解谓词(不确定),则每次调用时都会执行该方法。但是我在分配时输入的参数怎么样?这只用过一次吗?

2 个答案:

答案 0 :(得分:1)

谓词的定义是:

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

它所做的就是对obj进行某种类型的比较或测试,并返回true或false。我们一直在LINQ中看到这一点。

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

代表是&#34; p&#34;或参数,比较部分是谓词或bool值。请注意,传入的类型是&#34;暗示&#34;这是因为linq有一个静态类&#34;其中&#34;扩展方法允许最多传递任何集合类型,该类型将谓词作为参数。像这样:

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

根据Delegate命令在MSFT站点上的示例,我们看到新创建的一个,第二个parm传递一个名为&#34; CanSubmit&#34;的方法(指针)。在需要时被召唤。

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

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

答案 1 :(得分:0)

最后在完成代码的过程中弄明白了,一步一步地绊倒this question

原来那个

  

默认情况下,菜单项在其命令不可用时将被禁用   执行(CanExecute = false)。

(找不到MSDN中的任何引用?)

所以解决方案变得更加简单,因为我不再需要MenuItemViewModel上的IsEnabled属性。

我的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>

我的命令:

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