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, 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) ...
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?
Finally figured it out while going through the code, step by step and stumbling upon this question
Turns out that
(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:
And my commands: