In forms versions >2.3.2.127 I've run into an issue with command parameters not behaving as expected. For some reason the view model is passed into the CanExecute method of a ICommand object as a command parameter. This doesn't happen on Xamarin forms version 2.3.2.127.
I've attached a an example project that demonstrates the issue. I believe my binding is correct.
Here is some code stripped out of the example project for context.
<ListView ItemsSource="{Binding ItemSource}"
RowHeight="80"
SelectedItem="{Binding SelectedItem,
Mode=TwoWay}">
<x:Arguments>
<ListViewCachingStrategy>RecycleElement</ListViewCachingStrategy>
</x:Arguments>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.ContextActions>
<MenuItem Command="{Binding Path=BindingContext.TestCommand,
Source={x:Reference Name=MyMainPage}}"
CommandParameter="{Binding .}"
IsDestructive="True"
Text="Delete" />
</ViewCell.ContextActions>
<Label FontSize="24"
Text="{Binding .}" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
public class MainPageViewModel
{
public ObservableCollection<string> ItemSource { get; set; } = new ObservableCollection<string>();
public string SelectedItem { get; set; }
private RelayCommand<string> _TestCommand;
public RelayCommand<string> TestCommand => _TestCommand ?? (_TestCommand = new RelayCommand<string>(MyStringFunction));
private void MyStringFunction(string myString) => Debug.WriteLine(myString);
public MainPageViewModel()
{
for (var i = 0; i < 10; i++)
{
ItemSource.Add($"Test{i}");
}
}
}
public class RelayCommand<T> : ICommand
{
private readonly Action<T> _Execute;
private readonly Predicate<T> _CanExecute;
public RelayCommand(Action<T> execute, Predicate<T> canExecute = null)
{
if (execute == null) throw new ArgumentNullException(nameof(execute));
_Execute = execute;
_CanExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (parameter?.GetType() == typeof(MainPageViewModel))
{
throw new ArgumentException("ViewModel object passed into CanExecute this is not expected behavior")
}
return _CanExecute?.Invoke((T)parameter) ?? true;
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
public void Execute(object parameter) => _Execute((T)parameter);
}
public class RelayCommand : ICommand
{
readonly Action _Execute;
readonly Func<bool> _CanExecute;
public RelayCommand(Action execute, Func<bool> canExecute = null)
{
if (execute == null) throw new ArgumentNullException(nameof(execute));
_Execute = execute;
_CanExecute = canExecute;
}
[DebuggerStepThrough]
public bool CanExecute(object parameter) => _CanExecute?.Invoke() ?? true;
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
public void Execute(object parameter) => _Execute();
}
I sort of work around the issue by null checking the _CanExecute before the invalid cast happens. However; this will become an issue when passing a command parameter with a canExecute predicate.