Как создать делегат из лямбда-выражения с параметрами?

Я пытаюсь создать экземпляр RelayCommand с параметрами динамически:

public class RelayCommand<T> : ICommand
{
    #region Declarations

    private readonly Predicate<T> _canExecute;
    private readonly Action<T> _execute;

    #endregion

    #region Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="RelayCommand&lt;T&gt;"/> class and the command can always be executed.
    /// </summary>
    /// <param name="execute">The execution logic.</param>
    public RelayCommand(Action<T> execute)
        : this(execute, null)
    {
    }

    /// <summary>
    /// Initializes a new instance of the <see cref="RelayCommand&lt;T&gt;"/> class.
    /// </summary>
    /// <param name="execute">The execution logic.</param>
    /// <param name="canExecute">The execution status logic.</param>
    public RelayCommand(Action<T> execute, Predicate<T> canExecute)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");
        _execute = execute;
        _canExecute = canExecute;
    }

У меня есть ViewModel с несколькими методами, сейчас я просто перечислю:

public void MyMethod(object parameter);
public bool CanMyMethod(object parameter);

Я хочу подключить их динамически к экземпляру RelayCommand следующим образом:

ICommand command = new RelayCommand<ViewModel>((x)=>myviewmodel.MyMethod(myparameter),(x)=> myviewmodel.CanExecuteMyMethod(myparameter));

Предыдущая строка работает, однако, имена моих методов передаются во время выполнения, поэтому мне нужно добиться того же, но динамически.

РЕДАКТИРОВАТЬ: просто некоторые пояснения: в моем сценарии я не могу ссылаться на мои методы по имени напрямую. Имя метода, которое я буду использовать для создания RelayCommand, будет передано как STRING.

РЕШЕНИЕ:

Вот мое окончательное решение, используя предложение @ZafarYousafi. Обратите внимание, как я использую универсальный тип 'object' для моей RelayCommand и для Action и Predicate, так как это тип параметров моих методов (объект myparameter):

object myparameter = //Some value gets assigned here.
                Delegate td1 = null, td2 = null;
                MethodInfo method1 = myviewmodel.GetType().GetMethod("MyMethodNameAsString");

                if (tmethod1 != null)
                    td1 = Delegate.CreateDelegate(typeof(Action<object>), myviewmodel, method1);

                MethodInfo tmethod = viewmodel.GetType().GetMethod("Can" + "MyMethodNameAsString");
                if (method2 != null)
                    d2 = Delegate.CreateDelegate(typeof(Predicate<object>), myviewmodel, method2);

                if (d1 != null && d2 != null)
                {
                    item.Command = new RelayCommand<object>(obj => ((Action<object>) td1)(myparameter), obj => ((Predicate<object>)td2)(myparameter));
                }

Что должно быть эквивалентно:

item.Command = new RelayCommand<object>(param=>myviewmodel.MyMethod(myparameter),param=>myviewmodel.CanMyMethod(myparameter));

ВАЖНОЕ ПРИМЕЧАНИЕ. Как указывал @DanC, класс RelayCommand, созданный Джошем Смитом, не предназначен для получения параметров во время создания. В правильно спроектированном решении MVVM параметр RelayCommand будет передаваться через XAML-привязку свойства CommandParameter. Поэтому, если у вас есть кнопка. Команда, связанная с RelayCommand, вам также необходимо связать кнопку.CommandParameter, как описано здесь: MVVM RelayCommand с параметрами

СТАРАЯ неудачная попытка: вот что у меня так:

                Delegate d1 = null, d2 = null;
                MethodInfo method1 = myviewmodel.GetType().GetMethod("MyMethodNameAsString");
                if (method1 != null)
                    d1 = Delegate.CreateDelegate(typeof(Action<ViewModel>), myviewmodel, method1);

                MethodInfo method2 = myviewmodel.GetType().GetMethod("Can" + "MyMethodNameAsString");
                if (method2 != null)
                    d2 = Delegate.CreateDelegate(typeof(Predicate<ViewModel>), myviewmodel, method2);

                if (d1 != null && d2 != null)
                {
                    item.Command = new RelayCommand<ViewModel>((Action<ViewModel>)d1, (Predicate<ViewModel>)d2);
                }

Это работает нормально, без ошибок компиляции или времени выполнения, однако я не могу найти, как передать мой параметр через параметры конструктора RelayComand.

Любой совет будет очень признателен,

Спасибо

Связанный с моим предыдущим вопросом

4 ответа

Решение

Вы ввели приведение делегата в действие, и теперь у вас есть полная свобода для передачи параметров ((Action<ViewModel>)d1)(yourparameter)

Согласно коду, опубликованному в статье MVVM Джоша Смита. Вы должны использовать лямбда-переменную param для передачи параметра. В вашем примере вы вообще не используете лямбда-переменную "x". Эта переменная должна быть параметром для ваших методов Execute и CanExecute.

RelayCommand _saveCommand;
public ICommand SaveCommand
{
    get
    {
        if (_saveCommand == null)
        {
            _saveCommand = new RelayCommand(param => this.Save(),
                param => this.CanSave );
        }
        return _saveCommand;
    }
}

Предполагая, что команда создается внутри ViewModel, вы можете инициализировать ее следующим образом.

ICommand command = new RelayCommand<MyParameterType>((myparameter)=>this.MyMethod(myparameter),(myparameter)=> this.CanExecuteMyMethod(myparameter));

Поскольку вы не можете использовать лямбу для создания команды, ваш код будет выглядеть следующим образом.

Delegate d1 = null, d2 = null;
            MethodInfo method1 = myviewmodel.GetType().GetMethod("MyMethodNameAsString");
            if (method1 != null)
                d1 = Delegate.CreateDelegate(typeof(Action<YourParameterType>), myviewmodel, method1);

            MethodInfo method2 = myviewmodel.GetType().GetMethod("Can" + "MyMethodNameAsString");
            if (method2 != null)
                d2 = Delegate.CreateDelegate(typeof(Predicate<YourParameterType>), myviewmodel, method2);

            if (d1 != null && d2 != null)
            {
                item.Command = new RelayCommand<YourParameterType>((Action<YourParameterType>)d1, (Predicate<YourParameterType>)d2);
            }

Теперь после того, как команда была назначена объекту MenuItem, который в данном случае является ICommandSource, она вызовет двух ваших делегатов (d1,d2) с помощью CommandParameter.

Похоже, что на сайте, где RelayCommand экземпляр создан, у вас есть все необходимое для передачи делегатов из методов экземпляра myviemodel,

item.command = new RelayCommand<ViewModel>(
    myviemodel.MyMethod, myviewmodel.CanExecuteMyMethod)

Сценарий, который вы описываете, вероятно, работа для Delegate.DynamicInvoke но я не вижу необходимости в вашем фрагменте...

Просто определите метод в вашем классе RelayCommand для выполнения команды следующим образом:

public void Execute(T model)
    {
        if(_canExecute(model))
            _execute(model);
    }
Другие вопросы по тегам