Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic/type safe ICommand implementation?

I recently started using WPF and the MVVM framework, one thing that I have wanted to do is to have a type safe implementation of ICommand so I do not have to cast all the command paramaters.

Does anyone know of a way to do this?

like image 798
Alex Hope O'Connor Avatar asked Jun 08 '11 00:06

Alex Hope O'Connor


2 Answers

Not using that syntax, as you probably found:

error CS0701: ``System.Func`' is not a valid constraint. A constraint must be an interface, a non-sealed class or a type parameter

Your best bet is to encapsulate the Func<E,bool> semantics in an interface, like:

interface IFunctor<E>
{
   bool Execute(E value);
}

and then use this interface in the class definition. Although, I am wondering what you're looking to accomplish as there may be another approach to your problem.

Per the comment that @Alex is looking for a strongly typed ICommand implementation:

public FuncCommand<TParameter> : Command
{
    private Predicate<TParameter> canExecute;
    private Action<TParameter> execute;

    public FuncCommand(Predicate<TParameter> canExecute, Action<TParameter> execute)
    {
        this.canExecute = canExecute;
        this.execute = execute;
    }

    public override bool CanExecute(object parameter)
    {
        if (this.canExecute == null) return true;

        return this.canExecute((TParameter)parameter);
    }

    public override void Execute(object parameter)
    {
        this.execute((TParameter)parameter);
    }
}

Used like so:

public class OtherViewModel : ViewModelBase
{
    public string Name { get; set; }
    public OtherViewModel(string name) { this.Name = name; }
}

public class MyViewModel : ViewModelBase
{
    public ObservableCollection<OtherViewModel> Items { get; private set; }
    public ICommand AddCommand { get; private set; }
    public ICommand RemoveCommand { get; private set; }

    public MyViewModel()
    {
        this.Items = new ObservableCollection<OtherViewModel>();

        this.AddCommand = new FuncCommand<string>(
            (name) => !String.IsNullOrEmpty(name),
            (name) => this.Items.Add(new OtherViewModel(name)));
        this.RemoveCommand = new FuncCommand<OtherViewModel>(
            (vm) => vm != null,
            (vm) => this.Items.Remove(vm));
    }
}

XAML:

<ListBox x:Name="Items" ItemsSource="{Binding Items}" />
<Button Content="Remove"
        Command="{Binding RemoveCommand}"
        CommandParameter="{Binding SelectedItem, ElementName=Items}" />
<StackPanel Orientation="Horizontal">
    <TextBox x:Name="NewName" />
    <Button Content="Add"
            Command="{Binding AddCommand}"
            CommandParameter="{Binding Text, ElementName=NewName}" />
</StackPanel>

I would recommend using Microsoft's DelegateCommand or RelayCommand, or any other implementation of either of these.

like image 181
user7116 Avatar answered Nov 01 '22 06:11

user7116


Your command class should subscribe to ICommand and define the CanExecuteChanged as below.

public event EventHandler CanExecuteChanged
{
      add { CommandManager.RequerySuggested += value; }
      remove { CommandManager.RequerySuggested -= value; }
}
like image 37
falopsy Avatar answered Nov 01 '22 07:11

falopsy