Reputation: 865
I have some custom editable listbox on my wpf window. I also have a viewmodel class with Property Changed which looks like that:
public bool HasChanges
{
get
{
return customers.Any(customer => customer.Changed);
}
}
So, I would like to bind my Save button to this property:
<Button IsEnabled="{Binding HasChanges, Mode=OneWay}"...
My question is how to update Save button if one of the listbox rows is changed?
Upvotes: 6
Views: 1133
Reputation: 3333
The proper way to deal with buttons is to implement ICommand interface. Here is an example from my solution:
public class RelayCommand : ICommand
{
readonly Action<object> _execute;
readonly Predicate<object> _canExecute;
public RelayCommand(Action<object> execute) : this(execute, null)
{
}
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
#region ICommand Members
public bool CanExecute(object parameter)
{
return _canExecute == null ? true : _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
#endregion
}
You can then databind to button like this:
<Button Command="{Binding MyCommand}" .../>
Whats left is to declare an ICommand
property on your viewmodel:
public ICommand MyCommand { get; private set; }
//in constructor:
MyCommand = new RelayCommand(_ => SomeActionOnButtonClick(), _ => HasChanges);
The state of the button will then automatically update on most changes. If it doesnt for some reason - you can force the update by calling CommandManager.InvalidateRequerySuggested
Upvotes: 2
Reputation: 883
The button somehow has to receive notifications. In your case you probably implement the INotifyPropertyChanged interface in your viewmodel. When your "listbox row is changed" you should raise the PropertyChanged event for the "HasChanges" property. Changes should be noticed however in you viewmodel and there the event should be raised. As a different solution since you have a viewmodel you could use a command on your button and the CanExecute would have the logic returning true or false, which also has to be marked by you when changes have happened.
Upvotes: 0
Reputation: 727077
In order for WPF to react to changes in properties, the class must implement INotifyPropertyChanged
interface. You need to send notifications every time a customer is changed, like this:
class CustomerList : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private List<Customer> customers = ...
public bool HasChanges {
get {
return customers.Any(customer => customer.Changed);
}
}
// Callers who change customers inside your list must call this method
public void ChangeCustomer(Customer c) {
// Do whatever you need to do, ...
...
// then send out the notification to WPF
OnPropertyChanged("HasChanges");
}
protected void OnPropertyChanged(string name) {
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(name));
}
}
}
Upvotes: 2
Reputation: 227
If your ViewModel implements INotifyPropertyChanged, you just need to call the OnPropertyChanged() method on HasChanges. With Prism, the equivalent method is RaisePropertyChanged.
However, with MVVM, you might want to put that test in the CanExecute method of your command which is bound to your button Command property. This will handle the IsEnabled automatically.
Upvotes: 0
Reputation: 1108
Your ViewModel should implement INotifyPropertyChanged
and should raise the PropertyChanged Event for HasChanges
when ever you change a Customer in customers
Update:
If Customers implement INotifyPropertyChanged and customers it self is an observable collection. You could subscribe, and depending on the action desubscribe, to all the customers in the CollectionChangedEvent
of your customers
collection.
Upvotes: 1