Simple little implementation of INotifyPropertyChanged

Let's say I have the following class:

public MainFormViewModel { public String StatusText {get; set;} } 

What is the easiest way to get my StatusText changes to reflect any controls that are bound to it?

Obviously, I need to use INotifyPropertyChanged, but is there a cool way to do this so as not to clutter up my code? need a lot of files? etc.?

Note. If it's a hoax, I'm sorry. I searched and could not find anything, but with the help of T4 code Generation , which doesn’t sound so easy (at least for tuning).

+4
source share
6 answers

Unfortunately, C # does not offer a simple mechanism for this automatically ... It was suggested to create a new syntax:

 public observable int Foo { get; set; } 

But I doubt that it will ever be included in the language ...

A possible solution would be to use an AOP structure such as Postsharp , so you just need to decorate your properties with an attribute:

 public MainFormViewModel : INotifyPropertyChanged { [NotifyPropertyChanged] public String StatusText {get; set;} } 

(not tried, but I'm sure Postsharp allows you to do such things ...)


UPDATE: Well, I managed to get it to work. Please note that this is a very crude implementation, using reflection in the private field to extract the delegate ... Of course, this can be improved, but I will leave it to you;)

 [Serializable] public class NotifyPropertyChangedAttribute : LocationInterceptionAspect { public override void OnSetValue(LocationInterceptionArgs args) { object oldValue = args.GetCurrentValue(); object newValue = args.Value; base.OnSetValue(args); if (args.Instance is INotifyPropertyChanged) { if (!Equals(oldValue, newValue)) { RaisePropertyChanged(args.Instance, args.LocationName); } } } private void RaisePropertyChanged(object instance, string propertyName) { PropertyChangedEventHandler handler = GetPropertyChangedHandler(instance); if (handler != null) handler(instance, new PropertyChangedEventArgs(propertyName)); } private PropertyChangedEventHandler GetPropertyChangedHandler(object instance) { Type type = instance.GetType().GetEvent("PropertyChanged").DeclaringType; FieldInfo propertyChanged = type.GetField("PropertyChanged", BindingFlags.Instance | BindingFlags.NonPublic); if (propertyChanged != null) return propertyChanged.GetValue(instance) as PropertyChangedEventHandler; return null; } } 

Note that your class still needs to implement the INotifyPropertyChanged interface. You just do not need to explicitly raise the event in the property settings.

+4
source

Go through this http://code.google.com/p/notifypropertyweaver/

All you have to do is implement INotifyPropertyChanged

So your code will look like

 public MainFormViewModel : INotifyPropertyChanged { public String StatusText {get; set;} #region INotifyPropertyChanged Implementation } 

The build task will compile (you will never see the code below)

 public MainFormViewModel : INotifyPropertyChanged { public String StatusText {get; set;} private string statusText; public string StatusText { get { return statusText; } set { if (value!= statusText) { statusText = value; OnPropertyChanged("StatusText"); } } } #region INotifyPropertyChanged Implementation } 
+3
source

I always liked this method.

 private string m_myString; public string MyString { get { return m_myString; } set { if (m_myString != value) { m_myString = value; NotifyPropertyChanged("MyString"); } } } private void NotifyPropertyChanged(string property) { if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(property)); } 

or for a shorter code roll

 set { m_myString = value; NotifyPropertyChanged("MyString"); } 
+1
source

Using EqualityComparer.Default , you can reduce the property setting code to one line as follows:

 private int unitsInStock; public int UnitsInStock { get { return unitsInStock; } set { SetProperty(ref unitsInStock, value, "UnitsInStock"); } } public event PropertyChangedEventHandler PropertyChanged; protected void SetProperty<T>(ref T field, T value, string name) { if (!EqualityComparer<T>.Default.Equals(field, value)) { field = value; var handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(name)); } } } 

If your view models are inherited from the base class that defines the SetProperty method and the PropertyChanged event, then the amount of code needed to support INotifyPropertyChanged in your child’s view models becomes very minimal (1 line).

This approach is more detailed than the code weaving techniques mentioned in other answers, but does not require you to modify your build process to execute it.

Do not forget to look at the upcoming C # 5 attributes of caller information , and it also seems that they will allow us to avoid using the magic string in the method without the cost of executing the reflection.

UPDATE (March 1, 2012):

There is no beta version of .NET 4.5, and with it you can refine the code above to remove the need for a string literal in the caller:

 private int unitsInStock; public int UnitsInStock { get { return unitsInStock; } set { SetProperty(ref unitsInStock, value); } } public event PropertyChangedEventHandler PropertyChanged; private void SetProperty<T>(ref T field, T value, [CallerMemberName] string name = "") { if (!EqualityComparer<T>.Default.Equals(field, value)) { field = value; var handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(name)); } } } 

I have a blog post that talks about this in a bit more detail.

+1
source

I have a base class called "Model". It provides a protected object called DataPoints, which is essentially a dictionary.

WITH#

 public String StatusText { get { return (string)DataPoints["StatusText"]; } set { DataPoints["StatusText"] = value; } } 

B. B.

 public Property StatusText as String get return DataPoints!StatusText end get set DataPoints!StatusText = value end set end property 

When you set a value in the DataPoints dictionary, it does the following:

  • Checks if the value has really changed.
  • Saves the new value.
  • Sets the IsDirty property to true.
  • Raises the Modified Property event for the named property, as well as the IsDirty and IsValid properties.

Since this is a dictionary, it also makes it easy to load objects from a database or XML file.

Now you might think that reading and writing to the dictionary is expensive, but I tested a lot of performance testing and I did not find any noticeable effect of this in my WPF applications.

0
source

The PropertyChanged.Fody NuGet package does this.

https://github.com/Fody/PropertyChanged

  • Add the PropertyChanged.Fody package to your project.
  • PropertyChanged reference in your model: using PropertyChanged;
  • Add the [ImplementPropertyChanged] attribute to your class.

All properties in the class now magically implement INotifyPropertyChanged . Note. Fody works by modifying the emitted IL, so you'll never see the code in VS - it's just magical.

Additional documents: https://github.com/Fody/PropertyChanged/wiki/Attributes

0
source

All Articles