RelayCommand from lambda with constructor parameters

前端 未结 2 756
遇见更好的自我
遇见更好的自我 2021-01-19 18:23

If, in a XAML file, I bind a Button to \"Command\" from the following class, then clicking the Button does not cause DoIt to be executed:

class Thing()
{
  p         


        
相关标签:
2条回答
  • 2021-01-19 18:56

    your Problem is that calling the Method DoIt is inside another anonymous Method created by the lamda expression. Your expression

    () => DoIt(p1);
    

    creates a anonymous Method without parameters (seen as there are no variables provided in the first braces).

    I would recommend you to use the generic constructor from mvvm-light for creating the Command:

    class Thing
    {
        public Thing()
        {
           Command = new GalaSoft.MvvmLight.Command.RelayCommand<bool>(DoIt);
        }
    
        private void DoIt(bool p)
        {
           p.DoSomething(p);
        }
    
        public System.Windows.Input.ICommand Command { get; private set; }
    }
    

    Then just bind the Button to the "Command".

    0 讨论(0)
  • 2021-01-19 19:05

    It's an old question but I recently stumbled upon this topic and it's worth answering.

    The reason for this strange behavior originates from the MVVM Light implementation of RelayCommand. The execute and canexecute handlers are stored as WeakAction _execute and WeakFunc<bool> _canExecute in the relay command. The WeakAction is an attempt to allow the GC cleanup of viewmodels when the command is still referenced by the UI for some reason.

    Skipping some details, the bottom line is: assigning a viewmodel method as handler works great, because the WeakAction will stay alive as long as the viewmodel stays alive. For a dynamically created Action, the situation is different. If the only reference to that action is inside the RelayCommand, only a weak reference exists and GC can collect the action at any time, turning the whole RelayCommand into a dead brick.

    Ok, time for the details. The implementation of WeakAction is not blindly storing a weak reference to the action - this would lead to many disappearing references. Instead, a combination of a weak Delegate.Target reference and an Delegate.MethodInfo is stored. For a static method, the method will be stored by strong reference.

    Now, this leads to three categories of lambda:

    1. static method: () => I_dont_access_anything_nonstatic() will be stored as a strong reference
    2. closure on member variables: () => DoIt(field) the closure method will be created in the viewmodel class, the action target is the viewmodel and will stay alive as long as the viewmodel stays alive.
    3. closure on local variables: () => DoIt(p1) the closure will create a separate class instance to store the captured variables. This separate instance will be the action target and there won't be any strong reference to it - GC cleans up at some point

    Important: as far as I can tell, this behavior might change with Roslyn: Delegate caching behavior changes in Roslyn so there is a chance that todays working code with case (2) turns into non-working code with Roslyn. However, I didn't test this assumption, it might work out completely different.

    0 讨论(0)
提交回复
热议问题