2012-06-21 6 views
1

Я пытаюсь создать экземпляр 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)); 

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

EDIT: Только некоторые разъяснения: В моем сценарии я НЕ МОЖЕТ обращаться к моим методам по имени напрямую. Имя метода, которое я буду использовать для создания RelayCommand, будет выполнено как STRING.

РЕШЕНИЕ:

Вот мое окончательное решение, используя @ZafarYousafi предложение. Обратите внимание на то, как я использую универсальный тип «объект» для моего RelayCommand и действий и предиката, так что это тип моих параметров методов (объект 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. Так если у вас есть button.Command привязанный к RelayCommand также необходимо связать button.CommandParameter, как описано here: MVVM RelayCommand with parameters

OLD неудачная попытка: Это то, что я до сих пор:

   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.

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

Благодаря

, относящиеся к моей previous question

+0

ли RelayCommand имя метода или класса? отправьте полный код – Bond

+0

это класс от Джоша Смита, я верю, только что опубликовал его –

+1

u набрал команду делегата в действие, и теперь у вас есть полная свобода для передачи параметров ((Действие ) d1) (yourparameter) – ZafarYousafi

ответ

-2

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

+0

Это не работает для меня, это говорит о том, что: «Аргумент типа« тип моего параметра »не присваивается типу параметра ViewModel ' –

+0

Хорошо, я получил его работу. Мой тип параметра метода - это «объект», поэтому мне пришлось установить его для типа Action и Predicate Параметры RelayCommand. Я буду отмечать ваш ответ, поскольку он указал мне на правильное решение. Еще раз спасибо @Zafar_Yousafi –

+0

Я счастлив, что могу вам помочь. – ZafarYousafi

-1

Просто определить метод в вашем классе RelayCommand выполнить команду:

public void Execute(T model) 
    { 
     if(_canExecute(model)) 
      _execute(model); 
    } 
+0

Да, но это то, что я здесь пытался избежать. В общем, мне нравится .NET, но я нахожу, что он пытается заставить сделать методы и даже целые новые методы в ситуациях, когда это действительно не нужно. Многие из этих ситуаций, по-видимому, связаны с перечислением сравнения. – user1172763

1

Согласно коду, опубликованному в MVVM article Джошем Смитом. Вы должны использовать параметр переменной лямбда для передачи параметра. В вашем примере вы не используете «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)); 

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

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.

+0

Понял, но опять же, я не могу ссылаться на this.MyMethod непосредственно, потому что имя метода «MyMethod» будет поступать из моей базы данных (в виде строки), поэтому я не могу с жестким кодом ссылаться на него ... –

+1

Можете ли вы показать мне конструктор RelayCommand? Если он принимает только действие , Predicate , то вы можете заменить Object для родового типа вместо ViewModel. Затем, когда RelayCommand на самом деле вызывает Execute и CanExecute, он передаст параметр в действие и Predicate . –

+1

Извините, просто заметил, что он принимает эти параметры. Поскольку RelayCommand принимает действие , Predicate , вам не нужно передавать ему параметр при создании команды. Когда объект RelayCommand должен вызвать этих делегатов, он должен передать им параметр. Для того, чтобы это работало правильно, я думаю, вы хотите использовать Object как свой общий аргумент типа вместо ViewModel. –

0

Оказывается, что на месте, где построен RelayCommand экземпляра, у вас есть все, что нужно передать делегат от методы примера myviemodel.

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

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