3
Vote

RelayCommand fails if delegate contains a non-constant method parameter

description

I've come across what I think is a bug in RelayCommand (at least for WPF 4.0):
  • If you attempt to construct a RelayCommand with a lambda expression composed entirely of compile-time constants, e.g., () => DoSomething(2), it works as expected.
  • However, if the lambda expression contains a method call with a parameter, and the value passed into that parameter is determined at runtime, e.g., () => DoSomething(number), nothing happens when the command is executed.
If you put the code below into a WPF 4.0 project and build it in Debug mode, you will notice that if you click "Do Something", nothing happens, but if you click "Do Something Else", a "2" appears in the output window. (Similarly, if you put a breakpoint in the DoSomething method, it will only be hit if you click "Do Something Else".)

MainWindow.xaml:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApplication1"
        mc:Ignorable="d"
        x:Class="WpfApplication1.MainWindow"
        Title="MainWindow"
        Height="400"
        Width="400">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <StackPanel>
        <Button Content="Do Something"
                Command="{Binding DoSomethingCommand}" />
        <Button Margin="0,10,0,0"
                Content="Do Something Else"
                Command="{Binding DoSomethingElseCommand}" />
    </StackPanel>
</Window>
MainWindowViewModel.cs
namespace WpfApplication1
{
    public class MainWindowViewModel : ViewModelBase
    {
        public ICommand DoSomethingCommand { get; private set; }
        public ICommand DoSomethingElseCommand { get; private set; }

        public MainWindowViewModel()
        {
            DoSomethingCommand = GetCommand(2);
            DoSomethingElseCommand = new RelayCommand(() => DoSomething(2));
        }

        private ICommand GetCommand(int number)
        {
            return new RelayCommand(() => DoSomething(number));
        }

        private void DoSomething(int number)
        {
            Debug.Print(number.ToString());
        }
    }
}

comments

AlexPaskhin wrote May 18, 2013 at 4:07 PM

Here are links:
• MVVM-WPF XAML Mark-up Binding Extensions - http://wpfmvvmbindingext.codeplex.com
• MVVM-WPF XAML Mark-up Dependency Injection Binding Extensions - http://wpfmvvmbindingiocext.codeplex.com

They will help to solve

frogger3d wrote Apr 2, 2014 at 8:44 AM

I ran into the same issue.

We sometimes use our own WPF helper library which includes the RelayCommand by Josh Smith. That implementation does work in this specific case.

http://msdn.microsoft.com/en-us/magazine/dd419663.aspx

rdsman wrote Oct 1, 2014 at 3:30 PM

lbugnion wrote Dec 4, 2016 at 4:42 PM

Hi,

I just published an alpha version of V5.4.0 to Nuget. This version proposes a fix to this issue. This requires setting a new parameter called keepTargetAlive to true when you call Register. Note that while this allows using a closure, this also risks causing a (small) memory leak.

I have preliminary documentation here: [http://www.mvvmlight.net/doc/weakaction.cshtml]. A more detailed article will be published as soon as I have time to finish it..

Hopefully this helps. This is an alpha, which means I am interested on your feedback.

Thanks!
Laurent