24

The answer to this question has been edited to say that in C# 6.0, INotifyPropertyChanged can be implemented with the following OnPropertyChanged procedure:

protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } 

However, it isn't clear from that answer what the corresponding property definition should be. What does a complete implementation of INotifyPropertyChanged look like in C# 6.0 when this construction is used?

3
  • 1
    The other question / answer already contained all the bits... Each set would just be set { SetField(ref name, value); }. The SetField method was shown in full. Commented Feb 24, 2016 at 19:04
  • 2
    @MarcGravell, Yes, but it wasn't clear to me whether the C#5 and C#6 additions were meant to augment or supersede the SetField bit, and I can't request clarification on that question, so I had to ask a new question. I'm glad I did because seeing the entire class written out removes all ambiguity and makes it very easy to understand. Commented Feb 24, 2016 at 21:21
  • Actually, that's C# 5. Commented May 3, 2016 at 19:37

4 Answers 4

35

After incorporating the various changes, the code will look like this. I've highlighted with comments the parts that changed and how each one helps

public class Data : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { //C# 6 null-safe operator. No need to check for event listeners //If there are no listeners, this will be a noop PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } // C# 5 - CallMemberName means we don't need to pass the property's name protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null) { if (EqualityComparer<T>.Default.Equals(field, value)) return false; field = value; OnPropertyChanged(propertyName); return true; } private string name; public string Name { get { return name; } //C# 5 no need to pass the property name anymore set { SetField(ref name, value); } } } 
Sign up to request clarification or add additional context in comments.

15 Comments

What is result value of SetField used for?
It's true if a replacement was actually made and the event fired. It isn't used in this snippet
@panagiotis-kanavos: Regarding your suggestion that I communicate directly with the contributors to the other question, I'll go one step further and suggest that I should have posted my initial question as a comment under the answer there. However, the StackOverflow rules don't me to do either of those things, so this is the best I can do.
Why SetField returns true/false?
In c#6 you can also use lambda-like-syntax for getters and setters: get => name; set => SetField(ref name, value);
|
15

I use the same logic in my project. I have a base class for all view models in my app:

using System.ComponentModel; using System.Runtime.CompilerServices; public class PropertyChangedBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = "") { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } 

Every view model inherits from this class. Now, in the setter of each property I just need to call OnPropertyChanged().

public class EveryViewModel : PropertyChangedBase { private bool initialized; public bool Initialized { get { return initialized; } set { if (initialized != value) { initialized = value; OnPropertyChanged(); } } } 

Why does it work?

[CallerMemberName] is automatically populated by the compiler with the name of the member who calls this function. When we call OnPropertyChanged from Initialized, the compiler puts nameof(Initialized) as the parameter to OnPropertyChanged

Another important detail to keep in mind

The framework requires that PropertyChanged and all properties that you're binding to are public.

4 Comments

This could be a good answer to the original question, but a bad answer here. The OP asked how some existing code would look after multiple updates. At best, this should be a comment here
Besides, you miss the point of the original answer - instead of spreading hard-coded value checks and notifications calls everywhere, can you encapsulate all this in a generic way that allows custom equality comparisons ?
I disagree with the criticisms. Looks great to me. There are things you can do with comparisons, but... meh
I guess I emphasized on the code sample too much, and I should have emphasized "Why does it work?". Everyone, focus on "Why does it work?"
5

I know this question is old, but here is my implementation

Bindable uses a dictionary as a property store. It's easy enough to add the necessary overloads for a subclass to manage its own backing field using ref parameters.

  • No magic string
  • No reflection
  • Can be improved to suppress the default dictionary lookup

The code:

 public class Bindable : INotifyPropertyChanged { private Dictionary<string, object> _properties = new Dictionary<string, object>(); /// <summary> /// Gets the value of a property         /// <typeparam name="T"></typeparam> /// <param name="name"></param> /// <returns></returns> protected T Get<T>([CallerMemberName] string name = null) { object value = null; if (_properties.TryGetValue(name, out value)) return value == null ? default(T) : (T)value; return default(T); } /// <summary> /// Sets the value of a property /// </summary> /// <typeparam name="T"></typeparam> /// <param name="value"></param> /// <param name="name"></param> protected void Set<T>(T value, [CallerMemberName] string name = null) { if (Equals(value, Get<T>(name))) return; _properties[name] = value; OnPropertyChanged(name); } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } 

used like this

public class Item : Bindable { public Guid Id { get { return Get<Guid>(); } set { Set<Guid>(value); } } } 

8 Comments

There are 2 problems with this approach. Firstly watch out for boxing\unboxing of value types and secondly it loses type safety. A caller could call Set<string>("myProp") but Get<int>("myProp") which would cause an invalid cast exception. So it is down to the caller to ensure it is called correctly. Granted it's fairly safe to assume that a caller won't do that but still as a general rule you'd want to design that possibility out
@1adma12 boxing\unboxing? loses type safety? invalid cast exception? could you explain further those three? I use this code in many places and any chance to improve it would be great!
Info on boxing\unboxing in C# is available and the type safety issue is that the compiler won't stop you "Get"-ing an value of a different type to the one you original "Set"). Now arguably these are theoretical issues and if you are happy with the trade-offs there's not need to change anything. Indeed WPF's dependency properties work in a similar way to yours and they have accepted similar trade-offs. My point was more to explain why this isn't the generally accepted pattern for INotifyPropertyChanged. You will typically see properties backed with private fields.
@JasonLokiSmith agreed! I couldn't figure out the type safety issue or even how it could be one. thanks for the confirmation on that!
|
0

Instead of implementing the INotifyPropertyChanged manually in all classes, which can require a lot of boilerplate code, you can consider using tools that generate the code for you. Several tools are available:

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.