Search code examples
c#wpfmvvmbindingrelaycommand

Why does ViewModel get null when try pass Property from View through Command?


So I try pass MyProperty from code-behind of View through Command to ViewModel. its looks like this:

View: code-behind .xaml.cs

public partial class MainWindow : Window
{
    public string MyProperty { get; set; }
    
    public MainWindow()
    {
        InitializeComponent();
    }

    private void Test_Click(object sender, RoutedEventArgs e)
    {
        MyProperty = "test click";
    }
}

View: .xaml

  <MenuItem Header="Test" CommandParameter="{Binding ElementName=MainWindow, Path=MyProperty}" Command="{Binding Test}"  Click="Test_Click"></MenuItem>

ViewModel:

private RelayCommand _test;

public RelayCommand Test { 
    get 
    {
        if (_test != null)
        {
            return _test;
        }

        return _test = new RelayCommand(obj =>
        {
            MessageBox.Show($"{obj}"); // here obj = null why? 
        });
    }
}

RelayCommand:

internal class RelayCommand : ICommand
{
    private Action<object> _execute;
    private Func<object, bool> _canExecute;

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
    {
        _execute = execute;
        _canExecute = canExecute;
    }

    public bool CanExecute(object parameter)
    {
        return _canExecute == null || _canExecute(parameter);
    }

    public void Execute(object parameter)
    {
        _execute(parameter);
    }
}

also tried instead of ElementName use RelativeSource but doesnt work:

<MenuItem Command="{Binding DataContext.Test, RelativeSource={RelativeSource AncestorType=views:MainWindow}}" CommandParameter="{Binding RelativeSource={RelativeSource AncestorType=views:MainWindow}, Path=MyProp}" Click="Test_Click"></MenuItem>

My question is:

  1. why it doesnt work?
  2. Which order of binding? When I run debugging first will Event_Click, then CanExecute, and then Execute - here MyProperty already setted from Event_Click but anyway Command get null.

Solution

  • Binding only works on properties if you notify when the property value has changed. A simple property does not do any notification. Within a View you can declare a DependencyProperty which will do the notification for the binding

    public partial class MainWindow : Window
    {
        public string MyProperty
        {
            get { return (string)GetValue( MyPropertyProperty ); }
            set { SetValue( MyPropertyProperty, value ); }
        }
    
        // Using a DependencyProperty as the backing store for MyPropertyB.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty MyPropertyProperty =
            DependencyProperty.Register( "MyProperty", typeof( string ), typeof( MainWindow ), new PropertyMetadata( null ) );
    
        private void Button_Click( object sender, RoutedEventArgs e )
        {
            MyProperty = "From Button_Click";
        }
    }
    
    <Window
       ...
       x:Name="Root"
       ...>
        ...
        <Button Command="{Binding Test}"
                Content="Test"
                Click="Button_Click"
                CommandParameter="{Binding ElementName=Root, Path=MyProperty}" />
        ...
    </Window>