I have a Model
public class Irritant : BindableBase
{
private short _id;
private string _name;
private string _description;
public short Id
{
get { return _id; }
set { SetProperty(ref _id, value); }
}
public string Name
{
get { return _name; }
set { SetProperty(ref _name, value); }
}
public string Description
{
get { return _description; }
set { SetProperty(ref _description, value); }
}
public Irritant()
{
Id = 0;
Name = "";
Description = "";
}
}
Then my ViewModel with two versions
public class IrritantViewModel : BindableBase
{
private IrritantDb db = new IrritantDb();
//Version 1 - The Model's property is coded in IrritantViewModel
//private short _id;
//private string _name = "Alen";
//private string _description;
//public short Id
//{
// get { return _id; }
// set { SetProperty(ref _id, value); }
//}
//public string Name
//{
// get { return _name; }
// set { SetProperty(ref _name, value); }
//}
//public string Description
//{
// get { return _description; }
// set { SetProperty(ref _description, value); }
//}
//Version2 - I use the Irritant Model as property of IrritantViewModel
private DateTime? _lastUpdated;
private Irritant _entity;
public Irritant Entity
{
get { return _entity; }
set { SetProperty(ref _entity, value); }
}
public DateTime? LastUpdated
{
get { return _lastUpdated; }
set { SetProperty(ref _lastUpdated, value); }
}
public DelegateCommand UpdateCommand { get; set; }
public IrritantViewModel()
{
Entity = new Irritant();
//Version1
//UpdateCommand = new DelegateCommand(EditCommand, CanExecute).ObservesProperty(() => Name);
//Version2
UpdateCommand = new DelegateCommand(EditCommand, CanExecute).ObservesProperty(() => Entity.Name);
}
private bool CanExecute()
{
//Version1
//switch (Name)
//{
// case null:
// return false;
// case "":
// return false;
//}
//Version2
switch (Entity.Name)
{
case null:
return false;
case "":
return false;
}
return true;
}
private void EditCommand()
{
LastUpdated = DateTime.UtcNow;
}
}
And this is my View
public partial class IrritantView : UserControl
{
public IrritantView()
{
InitializeComponent();
DataContext = new IrritantViewModel();
}
}
<Grid >
<ScrollViewer>
<StackPanel MinWidth="200">
<TextBlock Text="Irritant" />
<!--Version 1-->
<!--<TextBlock Text="Name" />
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Description" />
<TextBox Text="{Binding Description, UpdateSourceTrigger=PropertyChanged}" />
-->
<!--Version 2-->
<TextBlock Text="Name" />
<TextBox Text="{Binding Entity.Name, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Description" />
<TextBox Text="{Binding Entity.Description, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock Text="Last Updated" />
<Label Content="{Binding LastUpdated, UpdateSourceTrigger=PropertyChanged}" />
<Button Content="Save"
Command="{Binding UpdateCommand}"
/>
</StackPanel>
</ScrollViewer>
</Grid>
The Version1 works fine, the Save Button disables when the TextBox that is bound to Name (TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}") is null or empty.
But with version 2, the Save Button doesn't disable. It only calls the method CanExecute during initialization, removing the text in the TextBox doesn't disable the Button. What did I do wrong?
DelegateCommand
doesn't raise CanExecuteChanged
event automatically, you have to raise that event manually by calling RaiseCanExecuteChanged
when appropriate. Apart from using DelegateCommand
, you can use RelayCommand
which relays on CommandManager.RequerySuggested
event which do the similar thing for you.
Change your Command definition returning ICommand
:
public ICommand UpdateCommand { get; set; }
Initialize the command using below:
UpdateCommand = new AutoCanExecuteCommand(new DelegateCommand(EditCommand, CanExecute));
Use the following class as a wrapper:
public class AutoCanExecuteCommand : ICommand
{
public ICommand WrappedCommand { get; private set; }
public AutoCanExecuteCommand(ICommand wrappedCommand)
{
if (wrappedCommand == null)
{
throw new ArgumentNullException("wrappedCommand");
}
WrappedCommand = wrappedCommand;
}
public void Execute(object parameter)
{
WrappedCommand.Execute(parameter);
}
public bool CanExecute(object parameter)
{
return WrappedCommand.CanExecute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}