如何使用参数从lambda表达式创建委托?

时间:2012-06-21 16:40:33

标签: c# reflection delegates lambda relaycommand

我正在尝试动态创建带有参数的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。

SOLUTION:

这是我的最终解决方案,使用@ZafarYousafi建议。注意我如何为我的RelayCommand以及Action和Predicate使用通用的'object'类型,因为这是我的方法参数的类型(对象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指出的那样,Josh Smith创建的RelayCommand类并不打算在创建时接收参数。在架构良好的MVVM解决方案中,RelayCommand参数将通过CommandParameter属性的XAML绑定传递。因此,如果你有一个button.Command绑定到一个RelayCommand,你还需要按照here: MVVM RelayCommand with parameters

的说明绑定button.CommandParameter。

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

相关

4 个答案:

答案 0 :(得分:1)

根据约什史密斯MVVM article发布的代码。您将使用lambda变量param传递参数。在您的示例中,您根本不使用“x”lambda变量。此变量应该是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)之后,它将使用CommandParameter调用您的两个代理(d1,d2)。

答案 1 :(得分:0)

看来,在构建RelayCommand实例的网站上,您已经拥有了从myviemodel实例的方法传递委托所需的一切。

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

您所描述的情景可能是Delegate.DynamicInvoke的工作,但我认为您的代码段不需要...

答案 2 :(得分:-1)

只需在RelayCommand类上定义一个方法来执行如下命令:

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

答案 3 :(得分:-2)

你已经键入了将委托投射到行动中,现在你可以完全自由地传递参数((Action<ViewModel>)d1)(yourparameter)