10

I'm trying to send a variable from the ViewModel as a parameter to a command. The command looks like this:

public class EditPersonCommand : ICommand
{
  private bool _CanExecute = false;

  public bool CanExecute(object parameter)
  {
     PersonModel p = parameter as PersonModel;
     CanExecuteProperty = (p != null) && (p.Age > 0);
     return CanExecuteProperty;
  }

  public event EventHandler CanExecuteChanged;

  public void Execute(object parameter) { }

  private bool CanExecuteProperty
  {
     get { return _CanExecute; }
     set
     {
        if (_CanExecute != value)
        {
           _CanExecute = value;
           EventHandler can_execute = CanExecuteChanged;
           if (can_execute != null)
           {
              can_execute.Invoke(this, EventArgs.Empty);
           }
        }
     }
  }
}

The ViewModel looks like this:

public class PersonViewModel : ViewModelBase
{
  private PersonModel _PersonModel;
  private EditPersonCommand _EditPersonCommand;

  ///<remarks>
  /// must use the parameterless constructor to satisfy <Window.Resources>
  ///</remarks>
  public PersonViewModel()
     : this(new PersonModel())
  {

  }

  public PersonViewModel(PersonModel personModel)
  {
     _PersonModel = personModel;
  }

  public ICommand EditPersonCommand
  {
     get
     {
        if (_EditPersonCommand == null)
        {
           _EditPersonCommand = new EditPersonCommand();
        }
        return _EditPersonCommand;
     }
  }
}

The xaml looks like this:

<Button Content="Edit" HorizontalAlignment="Right" Height="20" Width="80"
   Command="{Binding EditPersonCommand}" 
   CommandParameter="{Binding _PersonModel}" />

I've tried creating a property in the ViewModel instead of using the private local variable name, but that didnt work either. The object parameter always shows null in the call to CanExecute and the button is never enabled. If I change the CommandParameter value to Hello, then I receive Hello in the call to CanExecute, so I'm not sure why the variable doesnt work. Any help would be appreciated.

Update: I've also tried making a public property to the model (which I dont really want to expose the model, but just tried it to see if it works, but it doesnt).

// Added this to the ViewModel
public PersonModel PersonModelProp
{
  get
  {
     return _PersonModel;
  }
  set
  {
     _PersonModel = value;
     OnPropertyChanged("PersonModelProp");
  }
}

And changed the xaml to this:

<Button Content="Edit" HorizontalAlignment="Right" Height="20"  Width="80"
   Command="{Binding EditPersonCommand}" 
   CommandParameter="{Binding PersonModelProp}" />

But still no luck. The ViewModel does implement INotifyPropertyChanged

SwDevMan81
  • 48,814
  • 22
  • 151
  • 184
  • I always make bound properties `public`, I think they must be but not sure about it. – Alex Sep 11 '12 at 13:51
  • Not the cause of problem, but raising `CanExecuteChanged` from `CanExecute` is wrong. `CanExecuteChanged` should be raised when the caller should call `CanExecute` again. Based on your current `CanExecute` implementation, you should raise `CanExecuteChanged` when a person's age is changed, but you'll probably get away with never raising that event at all, too. –  Sep 11 '12 at 13:55
  • Have you tried [tracing](http://msdn.microsoft.com/en-us/library/dd409960.aspx)? What error do yo see? – Dylan Meador Sep 11 '12 at 14:06

5 Answers5

19

Is the CommandParameter always null or are you only checking the first time it is being executed?

It appears that the order in which you declare your properties matters in this case since setting the Command property causes the CanExecute to fire immediately before the CommandParameter has been set.

Try moving the CommandParameter property before the Command property:

<Button Content="Edit" HorizontalAlignment="Right" Height="20"  Width="80"
 CommandParameter="{Binding PersonModelProp}" 
 Command="{Binding EditPersonCommand}" />

Also, see here and here.

Edit

To ensure that your events are being raised properly you should raise the CanExecuteChanged event when the PersonModelProp value changes.

The Command:

public class EditPersonCommand : ICommand
{
  public bool CanExecute(object parameter)
  {
     PersonModel p = parameter as PersonModel;
     return p != null && p.Age > 0;
  }

  public event EventHandler CanExecuteChanged;

  public void Execute(object parameter) 
  {
      //command implementation
  }

  public void RaiseCanExecuteChanged()
  {
      var handler = CanExecuteChanged;
      if(handler != null)
      {
          handler(this, EventArgs.Empty);
      }
  }
}

And the view model:

public class PersonViewModel : ViewModelBase
{
  private PersonModel _PersonModel;
  private EditPersonCommand _EditPersonCommand;

  ///<remarks>
  /// must use the parameterless constructor to satisfy <Window.Resources>
  ///</remarks>
  public PersonViewModel()
     : this(new PersonModel())
  {
      _EditPersonCommand = new EditPersonCommand();
  }

  public PersonViewModel(PersonModel personModel)
  {
     _PersonModel = personModel;
  }

  public ICommand EditPersonCommand
  {
     get
     {
         return _EditPersonCommand;
     }
  }

  public PersonModel PersonModelProp
  {
      get
      {
         return _PersonModel;
      }
      set
      {
         _PersonModel = value;
         OnPropertyChanged("PersonModelProp");
         EditPersonCommand.RaiseCanExecuteChanged();
      }
    }
}
Community
  • 1
  • 1
Dylan Meador
  • 2,381
  • 1
  • 19
  • 32
  • Yikes. Looking at what `Button` does, your answer is a definite improvement but still incomplete. `Command.CanExecute` is never called when `CommandParameter` changes, so if `PersonModelProp` can change when `EditPersonCommand` doesn't, you won't get the behaviour you're hoping for. –  Sep 11 '12 at 17:15
  • @hvd You are correct. Like you mentioned in your original comment, a change should be made to raise the `CanExecuteChanged` event whenever the `PersonModelProp` property changes. – Dylan Meador Sep 11 '12 at 19:27
7

Two points to the answer:

First, as @akton mentioned, you can only bind to public properties. It doesn't have to be a DependencyProperty though.

Second, which took me some tome to figure out, is that you have to set the binding for the CommandParameter before the Command property. i.e.

<Button Content="Edit" HorizontalAlignment="Right" Height="20"  Width="80"
        CommandParameter="{Binding PersonModelProp}"
        Command="{Binding EditPersonCommand}" />

Hope this helps :)

AbdouMoumen
  • 3,814
  • 1
  • 19
  • 28
  • 1
    In Visual Studio 2015 Community, any attempt to move `CommandParamater` above `Command` will be reversed upon saving the document. In other words, saving the document forces the `CommandParameter` beneath the `Command`. This is default VS2015 behaviour, as it it a largely unmodified installation. – René Kåbis Sep 16 '17 at 07:11
4

_PersonModel is private and so cannot be accessed. Create a public property that exposes it and bind to that in the CommandParameter. Remember to make the property a dependency property (technically not required but it helps) and the ViewModel should implement INotifyProperty changed and fire the PropertyChanged event so the binding is updated.

akton
  • 14,148
  • 3
  • 43
  • 47
  • Yeah I tried making it a public property, but that didnt work either. – SwDevMan81 Sep 11 '12 at 13:53
  • @SwDevMan81 Was it a dependency property? Did you fire the PropertyChanged event? – akton Sep 11 '12 at 13:55
  • See my update. It is not a dependency property, but the ViewModel does implement `INotifyPropertyChanged` and the `PropertyChanged` event is called. Any other ideas? – SwDevMan81 Sep 11 '12 at 14:01
  • @SeDevMan81 Time for some binary debugging. Change PersonModelProp to return a hard coded int and see whether that works. If it does, it's something wrong with the binding. If it does not work, there might be a typo in the XAML or something. – akton Sep 11 '12 at 14:12
  • If I change the Prop to an int, it still hows the parameter as null, however if I put the CommandParameter before the Command binding as others suggested, it works. Thats strange. Thx for the help, time to figure out why the order matters... – SwDevMan81 Sep 11 '12 at 14:51
  • @SwDevMan81 That is strange. Please update the post when you find out. I, for one, would love to know why. – akton Sep 12 '12 at 01:03
3

I think you have a problem in your EditPersonCommand (it not fired ok).I check it with relayCommand and it work!

This is the code:

ViewModel:

 public class PersonViewModel : ViewModelBase
    {
        private PersonModel _PersonModel;
        private ICommand _EditPersonCommand;

        ///<remarks>
        /// must use the parameterless constructor to satisfy <Window.Resources>
        ///</remarks>
        public PersonViewModel()
            : this(new PersonModel())
        {

        }

        public PersonViewModel(PersonModel personModel)
        {
            PersonModelProp = personModel;
        }

        public ICommand EditPersonCommand
        {
            get
            {
                if (_EditPersonCommand == null)
                {
                    _EditPersonCommand = new RelayCommand(ExecuteEditPerson,CanExecuteEditPerson);
                }
                return _EditPersonCommand;
            }
        }


        private bool CanExecuteEditPerson(object parameter)
        {
            PersonModel p = parameter as PersonModel;

            return (p != null) && (p.Age > 0);
        }


        private void ExecuteEditPerson(object o)
        {

        }


        public PersonModel PersonModelProp
        {
            get
            {
                return _PersonModel;
            }
            set
            {
                _PersonModel = value;
                NotifyPropertyChanged("PersonModelProp");
            }
        }


    }

And this RelayCommand (Fire events ok!)

      public class RelayCommand : ICommand
        {
            #region Constants and Fields


            private readonly Predicate<object> canExecute;


            private readonly Action<object> execute;

            #endregion

            #region Constructors and Destructors


            public RelayCommand(Action<object> execute)
                : this(execute, null)
            {
            }

            public RelayCommand(Action<object> execute, Predicate<object> canExecute)
            {
                if (execute == null)
                {
                    throw new ArgumentNullException("execute");
                }

                this.execute = execute;
                this.canExecute = canExecute;
            }

            #endregion

            #region Events


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

                remove
                {
                    CommandManager.RequerySuggested -= value;
                }
            }

            #endregion

            #region Implemented Interfaces

            #region ICommand


            [DebuggerStepThrough]
            public bool CanExecute(object parameter)
            {
                return this.canExecute == null || this.canExecute(parameter);
            }

            public void Execute(object parameter)
            {
                this.execute(parameter);
            }

            #endregion

            #endregion
        }

Xmal:

<Button Content="Edit" HorizontalAlignment="Right" Height="20"  Width="80"
 CommandParameter="{Binding PersonModelProp}" 
 Command="{Binding EditPersonCommand}" />
ígor
  • 1,144
  • 7
  • 16
0

The command argument must allow null

[RelayCommand]
private void ClickForm(int? id)
{
    MessageBox.Show(id.ToString());
}