Quantcast
Channel: Recent Threads — Xamarin Community Forums
Viewing all articles
Browse latest Browse all 204402

Unexpected UWP listview context action command parameter behavior

$
0
0

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.


Viewing all articles
Browse latest Browse all 204402

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>