Question

I'm trying to create an instance of a RelayCommand with parameters dynamically:

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;
    }

I have a ViewModel with multiple methods, for now I'll just list:

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

I want to hook them dynamically to an instance of RelayCommand as follows:

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

The previous line works, however, my Method names are passed in at runtime so I need to achieve the same thing but dynamically.

EDIT: Just Some Clarification: In my scenario I CANNOT refer to my methods by name directly. The Method Name that I will use to create a RelayCommand WILL BE PASSED AS STRING.

SOLUTION:

Here is my final solution, using @ZafarYousafi suggestion. Notice how I use a generic 'object' type for my RelayCommand and for Action and Predicate since that is the type of my methods parameters (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));
                }

Which should be equivalent to:

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

IMPORTANT NOTE: As @DanC pointed out, the RelayCommand class created by Josh Smith is not intended to receive parameters at time of creation. In a well-architected MVVM Solution the RelayCommand parameter would be passed through XAML binding of CommandParameter property. So if you have a button.Command bound to a RelayCommand you also need to bind the button.CommandParameter as explained here: MVVM RelayCommand with parameters

OLD unsuccessful Attempt: This is what I have so far:

                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);
                }

That runs fine, no compilation or run-time errors, however I don't find how to pass my parameter through the RelayComand constructor parameters.

Any advice will be very appreciated,

Thanks

Related to my previous question

Était-ce utile?

La solution

u have typed cast the delegate into action and now u have full freedom to pass parameters ((Action<ViewModel>)d1)(yourparameter)

Autres conseils

According the code posted in the MVVM article by Josh Smith. You would use the lambda variable param to pass the parameter. In your example you are not using the "x" lambda variable at all. This variable should be the parameter for you Execute and CanExecute methods.

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

Assuming the command is created within the ViewModel then you would initialize it as follows.

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

Since your not able to use a lamba to construct the command your code would be as follows.

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);
            }

Now after the command has been assigned to the MenuItem object, which is the ICommandSource in this case, it will invoke your two delegates (d1,d2) with the CommandParameter.

It appears that on the site where RelayCommand instance is constructed, you've got everything you need to pass delegates from the methods of instance of myviemodel.

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

The scenario you are describing is probably a job for Delegate.DynamicInvoke but I don't see a need for that in your snippet...

Just define a method on your RelayCommand class to execute the command like this:

public void Execute(T model)
    {
        if(_canExecute(model))
            _execute(model);
    }
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top