ArgumentNullException when binding menuitem IsEnabled to ICommand CanExecute

636 views Asked by At

I'm trying to disable a menuitem depending on objects in an 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}" />

When opening the File-menu, I get an exception.

System.ArgumentNullException was unhandled HResult=-2147467261
Message=Value cannot be null. Parameter name: source
Source=System.Core
ParamName=source
StackTrace: at System.Linq.Enumerable.Any[TSource](IEnumerable1 source, Func2 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) ...

At first I tought the reason was the fact that there are no items in MenuItems at the start. However, when I run the folowing code after my menu-creation it returns false (as expected).

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 ?

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?

2

There are 2 answers

1
Koen On BEST ANSWER

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).

(Could not find any reference to that in MSDN??)

So the solution became a lot simpler since I didn't need the IsEnabled property on my MenuItemViewModel anymore.

My XAML looks now like:

<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); }));
        }
    }
1
JWP On

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. We see this all the time in 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. 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.

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

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