Reputation: 20004
Is there some way to avoid this. I have a lot of classes that are bound to DataGridViews and they are just simple collection of properties with default getter and setter. So these classes are very simple. Now I need to implement INotifyPropertyChanged interface for them which will increase the amount of code a lot. Is there any class that I can inherit from to avoid writing all this boring code? I image that I can inherit my classes from some class and decorate the properties with some attributes and it will do the magic. Is that possible?
I'm well aware of Aspect Oriented Programming, but I'd rather do it object oriented way.
Upvotes: 8
Views: 2033
Reputation: 2818
improvements on leepie.
values
dictionary (which results in a KeyNotFoundException)
avoid passing the name
of the property ( using CallerMemberName
attribute )(["name"])
.usage:
public class MyViewModel : PropertyChangedHelper
{
public int Bar
{
get => GetProperty<int>();
set => SetProperty<int>(value);
}
}
code:
public class PropertyChangedHelper : INotifyPropertyChanged
{
private readonly Dictionary<string, object> _values = new Dictionary<string, object>();
public event PropertyChangedEventHandler PropertyChanged;
public T GetProperty<T>([CallerMemberName] string propertyName = "")
{
EnsureElement<T>(propertyName);
return (T) _values[propertyName];
}
public void SetProperty<T>(object value, [CallerMemberName] string propertyName = "")
{
EnsureElement<T>(propertyName);
if (_values[propertyName] == value)
{
return;
}
_values[propertyName] = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void EnsureElement<T>(string propertyName)
{
if (!_values.ContainsKey(propertyName))
{
_values.Add(propertyName, default(T));
}
}
}
from : devto there is also benchmark in there.
Upvotes: 0
Reputation: 7719
Here is a similar solution to Marc, that has been extended to allow multiple property onpropertychanges and multiple RaiseCanExecuteChanged
simplest example usage
string _firstName;
public string FirstName
{
get { return _firstName; }
set { OnPropertyChanged(ref _firstName, value, "FirstName"); }
}
advanced example using multiple property updates and multiple commands
string _firstName;
public string FirstName
{
get { return _firstName; }
set { OnPropertyChanged(ref _firstName, value, "FirstName", "FullName", Command1, Command2); }
}
The advanced example calls OnProperty changed on firstname and fullname and also calls RaiseCanExecuteChanged for command1 and command2
base ViewModel code
protected void OnPropertyChanged<T>(ref T field, T value, params object[] updateThese)
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
OnPropertyChanged(updateThese);
}
}
protected void OnPropertyChanged(params object[] updateThese)
{
if (PropertyChanged != null)
{
foreach (string property in updateThese.Where(property => property is string))
PropertyChanged(this, new PropertyChangedEventArgs(property));
foreach (DelegateCommand<object> command in updateThese.Where(property => property is DelegateCommand<object>))
command.RaiseCanExecuteChanged();
}
}
Upvotes: 1
Reputation: 51897
I have just found ActiveSharp - Automatic INotifyPropertyChanged, I have yet to use it, but it looks good.
To quote from it's web site...
Send property change notifications without specifying property name as a string.
Instead, write properties like this:
public int Foo
{
get { return _foo; }
set { SetValue(ref _foo, value); } // <-- no property name here
}
Note that there is no need to include the name of the property as a string. ActiveSharp reliably and correctly figures that out for itself. It works based on the fact that your property implementation passes the backing field (_foo) by ref. (ActiveSharp uses that "by ref" call to identify which backing field was passed, and from the field it identifies the property).
Upvotes: 0
Reputation: 3940
Without AOP, I don't think there is an easy way to retrofit this to your existing classes. However you do it, you're at the very least going to have to change all your properties.
I use a base class inheriting INotifyPropertyChanged with an OnPropertyChanged(string propertyName) method to fire the event. I then use a Visual Studio Code snippet to create properties that automatically call OnPropertyChanged in the property setter.
Upvotes: 1
Reputation: 27419
Using code gen (say, T4) is another way. Check the discussion at: Automatic INotifyPropertyChanged Implementation through T4 code generation?.
I use this method, and it works well.
Upvotes: 0
Reputation: 117220
Create a container base class, eg:
abstract class Container : INotifyPropertyChanged
{
Dictionary<string, object> values;
protected object this[string name]
{
get {return values[name]; }
set
{
values[name] = value;
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
class Foo : Container
{
public int Bar
{
{get {return (int) this["Bar"]; }}
{set { this["Bar"] = value; } }
}
}
Note: very simplified code
Upvotes: 5
Reputation: 1062725
It depends; you could use PostSharp to write such an attribute that is re-written by the weaver; however, I would be tempted to just do it manually - perhaps using a common method for handling the data updates, i.e.
private string name;
public string Name {
get { return name; }
set { Notify.SetField(ref name, value, PropertyChanged, this, "Name"); }
}
with:
public static class Notify {
public static bool SetField<T>(ref T field, T value,
PropertyChangedEventHandler handler, object sender, string propertyName)
{
if(!EqualityComparer<T>.Default.Equals(field,value)) {
field = value;
if(handler!=null) {
handler(sender, new PropertyChangedEventArgs(propertyName));
}
return true;
}
return false;
}
}
Upvotes: 9