WPF how to update CanExecute
I have the following question.
I have the following simple xaml:
<TextBox Name="NameBox" Text ="{Binding Name}" />
<Button Content="Save" Command="{Binding SaveCommand}" CommandParameter="{Binding Entity}" />
And I bind the DataContext of this window to the following viewmodel
public class MyViewModel
{
public SimpleModel Entity { get; set; }
private ICommand _saveCommand;
public ICommand SaveCommand { get { return _saveCommand ?? (_saveCommand = new MyCommand(OnSaveItem, parameter => CanSaveItem())); } }
public void OnSaveItem(object parameter)
{
// some code
}
public virtual bool CanSaveItem()
{
return !String.IsNullOrWhiteSpace(Entity.Name);
}
}
SimpleModel -
public class SimpleModel
{
public int Id { get; set; }
public string Name { get; set; }
}
This code works mostly correctly, but I cannot get the CanSaveItem method to work correctly. I don't know how to tell SaveCommand that the ViewModel properties have changed. I know I need to use CanExecuteChanged or CommandManager.InvalidateRequerySposed and I tried to use them several times, but I don't know how to do it correctly and it didn't make any difference. Could you please help me with this problem?
UPD.
public class MyCommand : ICommand
{
public MyCommand(Action<object> execute, Predicate<object> canExecute)
{
_canExecute = canExecute;
_execute = execute;
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
}
source to share
It looks like you are on an early learning curve and it can be confusing ... and sometimes still for me too
Anyway, I made some changes to what you had and explained what I did with them.
public class MyViewModel
{
public SimpleModel Entity { get; set; }
private MyCommand _saveCommand;
public MyCommand SaveCommand { get { return _saveCommand ?? (_saveCommand = new MyCommand(OnSaveItem, parameter => CanSaveItem())); } }
public MyViewModel()
{
//------ You need to create an instance of your entity to bind to
Entity = new SimpleModel();
//-- I added an event handler as your "Entity" object doesn't know
//-- about the button on the view model. So when it has something
//-- change, have it call anybody listening to its exposed event.
Entity.SomethingChanged += MyMVVM_SomethingChanged;
}
void MyMVVM_SomethingChanged(object sender, EventArgs e)
{
// Tell our mvvm command object to re-check its CanExecute
SaveCommand.RaiseCanExecuteChanged();
}
public void OnSaveItem(object parameter)
{
// some code
}
public virtual bool CanSaveItem()
{
//-- Checking directly to your Entity object
return !String.IsNullOrWhiteSpace(Entity.Name);
}
}
public class SimpleModel
{
//-- Simple constructor to default some values so when you run
//-- your form, you SHOULD see the values immediately to KNOW
//-- the bindings are correctly talking to this entity.
public SimpleModel()
{
_name = "test1";
_Id = 123;
}
//-- changed to public and private... and notice in the setter
//-- to call this class "somethingChanged" method
private int _Id;
public int Id
{
get { return _Id; }
set
{
_Id = value;
somethingChanged("Id");
}
}
private string _name;
public string Name
{ get { return _name; }
set { _name = value;
somethingChanged( "Name" );
}
}
//-- Expose publicly for anything else to listen to (i.e. your view model)
public event EventHandler SomethingChanged;
//-- So, when any property above changes, it calls this method with whatever
//-- its property is just as a reference. Then checks. Is there anything
//-- listening to our exposed event handler? If so, pass the information on
private void somethingChanged( string whatProperty)
{
// if something is listening
if (SomethingChanged != null)
SomethingChanged(whatProperty, null);
}
}
public class MyCommand : ICommand
{
public MyCommand(Action<object> execute, Predicate<object> canExecute)
{
_canExecute = canExecute;
_execute = execute;
}
public bool CanExecute(object parameter)
{
return _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
private readonly Predicate<object> _canExecute;
private readonly Action<object> _execute;
//-- Change to the event handler definition, just expose it
public event EventHandler CanExecuteChanged;
//-- Now expose this method so your mvvm can call it and it rechecks
//-- it own CanExecute reference
public void RaiseCanExecuteChanged()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, new EventArgs());
}
}
Finally, the bindings in the form. I don't know how you set your view's "DataContext" to your viewmodel, but assuming everything is correct and not a problem, adjust the textarea and command button to something like
<TextBox Name="NameBox" Text ="{Binding Entity.Name,
NotifyOnTargetUpdated=True, UpdateSourceTrigger=PropertyChanged}" />
<Button Content="Save" Command="{Binding SaveCommand}" CommandParameter="{Binding Entity}" />
Note that binding the text to the "Entity" object on your MVVM and then the ".Name" property of the Entity object. The UpdateSourceTrigger is important here. This forces the update to revert to the data binding for every character change, so as soon as you delete the last character or start typing the first character, the Save button will then update accordingly.
source to share