Generic ICommand error

Emon Haque 3,176 Reputation points
2020-10-29T13:07:03.41+00:00

I've these ICommand:

public ICommand Add { get; set; }
public ICommand Remove { get; set; }

initialized this way:

Add = new Command<Item>(add, (o) => true);
Remove = new Command<IEnumerable<Item>>(remove, (o) => true);

when I launch the application I get this error:

System.InvalidCastException: 'Unable to cast object of type 'System.Windows.Controls.SelectedItemCollection' to type 'System.Collections.Generic.IEnumerable`1[ExpWPF.Item]'.'

CommandParameter for the Remove command is SelectedItems of a ListBox.

<Button Content="Remove" Command="{Binding Remove}" CommandParameter="{Binding ElementName=listBox, Path=SelectedItems}"/>

it works if I pass IEnumerable<object> instead of IEnumerable<Item> in template! How can I use Item instead of object here?

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,765 questions
{count} votes

Accepted answer
  1. Peter Fleischer (former MVP) 19,321 Reputation points
    2020-11-02T05:40:40.157+00:00

    Hi,
    for CanExecute you must implement the same code:

      public class Command<T> : ICommand
      {
        private readonly Action<T> _execute;
        private readonly Predicate<T> _canExecute;
        public Command(Action<T> _) { _execute = _; _canExecute = null; }
        public Command(Action<T> execute, Predicate<T> _) { this._canExecute = _; this._execute = execute; }
        public void Execute(object _)
        {
          if (_ is IEnumerable)
          {
            var para = (IEnumerable)_;
            var type = typeof(T);
            var list = Activator.CreateInstance(type);
            foreach (var item in para) type.GetMethod("Add").Invoke(list, new[] { item });
            this._execute((T)list);
          }
          else this._execute((T)_);
        }
    
        public bool CanExecute(object _)
        {
          if (this._canExecute == null) return true;
          if (_ is IEnumerable)
          {
            var para = (IEnumerable)_;
            var type = typeof(T);
            var list = Activator.CreateInstance(type);
            foreach (var item in para) type.GetMethod("Add").Invoke(list, new[] { item });
            return this._canExecute((T)list);
          }
          return this._canExecute((T)_);
        }
        public event EventHandler CanExecuteChanged
        {
          add { CommandManager.RequerySuggested += value; }
          remove { CommandManager.RequerySuggested -= value; }
        }
      }
    
    1 person found this answer helpful.

3 additional answers

Sort by: Most helpful
  1. Peter Fleischer (former MVP) 19,321 Reputation points
    2020-10-30T11:50:46.977+00:00

    Hi,
    you can check parameter inCommand class.

     Add = new Command<Item>(add, (o) => true);
     Remove = new Command<Item>(remove, (o) => true);
    

    and Command class:

      public class Command<T> : ICommand
      {
        private readonly Predicate<object> _canExecute;
        private readonly Action<object> _action;
        public Command(Action<object> action) { _action = action; _canExecute = null; }
        public Command(Action<object> action, Predicate<object> canExecute) { _action = action; _canExecute = canExecute; }
        public void Execute(object o)
        {
          if (o is T) { _action((T)o); return; }
          if (o is IEnumerable) { _action(((IEnumerable)o).Cast<T>()); return; }
        }
        public bool CanExecute(object o) => _canExecute == null ? true : _canExecute(o);
        public event EventHandler CanExecuteChanged
        {
          add { CommandManager.RequerySuggested += value; }
          remove { CommandManager.RequerySuggested -= value; }
        }
      }
    
    1 person found this answer helpful.

  2. Emon Haque 3,176 Reputation points
    2020-10-30T12:23:33.23+00:00

    anonymous user-3316, if I define _action with object, I've to have object, instead of Item and IEnumerable<Item> , in both add and remove:

    void add(object o)
    void remove(object o)
    

    and as a result, I've to cast those object to Item or IEnumerable<Item>. With Action<T> _action, I can define add like void add(Item o) and there's no need to cast BUT that doesn't work for remove! I'd tried this:

    public class Command<T> : ICommand
    {
        Action<T> execute1;
        Action<IEnumerable<T>> execute2;
        public Command(Action<T> execute, ...)
        {
            execute1 = execute;
        }
        public Command(Action<IEnumerable<T>> execute, ...)
        {
            execute2 = execute;
        }
        public void Execute(object p)
        {
            if(p is IEnumerable) execute2(((IEnumerable<object>)p).Cast<T>());
            else execute1((T)p);
        }
    }
    

    BUT the 2nd constructor isn't called!

    0 comments No comments

  3. Emon Haque 3,176 Reputation points
    2020-11-01T12:20:15.983+00:00

    This works but it requires Reflection

    public class Command<T> : ICommand
    {
        Action<T> execute;
        Func<object, bool> canExecute;
        public Command(Action<T> execute, Func<object, bool> canExecute)
        {
            this.canExecute = canExecute;
            this.execute = execute;
        }
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
        public bool CanExecute(object parameter) => canExecute(null);
        public void Execute(object parameter)
        {
            if(parameter is IEnumerable)
            {
                var para = (IEnumerable)parameter;
                var type = typeof(T);
                var list = Activator.CreateInstance(type);              
                foreach (var item in para)
                    type.GetMethod("Add").Invoke(list, new[] { item });
                execute((T)list);
            }
            else execute((T)parameter);
        }
    }
    

    with this I can define Remove as:

    Remove = new Command<List<Item>>(remove, (o) => true);
    void remove(List<Item> o){ ... }
    

    it, probably, will consume more time than cast in the remove.

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.