Implementing Inotifypropertychanged - Does a Better Way Exist

Implementing INotifyPropertyChanged - does a better way exist?

Without using something like postsharp, the minimal version I use uses something like:

public class Data : INotifyPropertyChanged
{
// boiler-plate
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, string propertyName)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}

// props
private string name;
public string Name
{
get { return name; }
set { SetField(ref name, value, "Name"); }
}
}

Each property is then just something like:

private string name;
public string Name
{
get { return name; }
set { SetField(ref name, value, "Name"); }
}

which isn't huge; it can also be used as a base-class if you want. The bool return from SetField tells you if it was a no-op, in case you want to apply other logic.


or even easier with C# 5:

protected bool SetField<T>(ref T field, T value,
[CallerMemberName] string propertyName = null)
{...}

which can be called like this:

set { SetField(ref name, value); }

with which the compiler will add the "Name" automatically.


C# 6.0 makes the implementation easier:

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

...and now with C#7:

protected void OnPropertyChanged(string propertyName)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

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 => name;
set => SetField(ref name, value);
}

And, with C# 8 and Nullable reference types, it would look like this:

public event PropertyChangedEventHandler? PropertyChanged;

protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = "")
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}

private string name;
public string Name
{
get => name;
set => SetField(ref name, value);
}

Implementing INotifyPropertyChanged - does a better way exist without using AOP?

Without code generation i know only one solution - use Castle Dynamic Proxy and interceptor as described here: http://jonas.follesoe.no/oldblog/2009-12-23-automatic-inotifypropertychanged-using-dynamic-proxy/

Implement INotifyPropertyChanged in a Class

INotifyPropertyChanged is intended for view model classes, not for the views (or user controls) themselves. Therefore you don't normally need them in the views. You should use dependency properties instead, if you want to add fields to a user control.

See the example on UserControl:

/// <summary>
/// Identifies the Value dependency property.
/// </summary>
public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register(
"Value", typeof(decimal), typeof(NumericUpDown),
new FrameworkPropertyMetadata(MinValue, new PropertyChangedCallback(OnValueChanged),
new CoerceValueCallback(CoerceValue)));

/// <summary>
/// Gets or sets the value assigned to the control.
/// </summary>
public decimal Value
{
get { return (decimal)GetValue(ValueProperty); }
set { SetValue(ValueProperty, value); }
}

using of INotifyPropertyChanged

You need INotifyPropertyChanged if you want a wpf form to be automatically updated when a property changes through code. Also some controllers might want to know if edits have been made in order to enable/disable a save-button, for instance. You also might be displaying the same property on different views; in this case INotifyPropertyChanged helps to immediately update the other view when you edit a property.

If you think that your form behaves well without INotifyPropertyChanged, then you can drop it.

Note that binding works even without INotifyPropertyChanged. See: Why does the binding update without implementing INotifyPropertyChanged?


I would implement the properties like this. In some rare cases it can help to avoid endless circular updates. And it is more efficient by the way.

 private string _firstName;
public string StudentFirstName
{
get { return _firstName; }
set
{
if (value != _firstName) {
_firstName = value;
OnPropertyChanged("StudentFirstName");
}
}
}

Starting with C#6.0 (VS 2015), you can implement OnPropertyChanged like this:

private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

When implementing INotifyPropertyChanged, do navigation properties need to implement it too?

As you understand, you implement INotifyPropertyChanged (INPC) in order for the UI to update when a property on the model changes. So in your case, if you have something that is data binding to the Album property, it must implement INPC if there is a chance that it might change. Instead of using regular collection, you have a class called ObservableCollection that already implements INPC for you so you don't have to.

What's better? INotifyPropertyChanged or having separate *Changed events?

Going forward, INotifyPropertyChanged is the norm, and has much better support in WPF. I seem to recall that BindingList<T> only respects INotifyPropertyChanged (see HookPropertyChanged and UnhookPropertyChanged in reflector).

This is also more efficient, as the UI only needs one event hook, rather than one per event - and your class can be more efficient as it only needs a field for one handler (rather than either one per property, or the niggle of having to go via EventHandlerList and a set of static keys)

The old style is mainly a hangover.

INotifyPropertyChanged best practices

In .Net the preferred practice for methods that raise events is for the method to be declared as protected so that it can only be called by derived classes (This is because only the class that declares the event can raise it. In order to raise the event from a derived class a method is required to raise the event).

For example...

protected void OnPropertyChanged(string propertyName)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}

This method is then called by the class (or derived classes) in a property setter to indicate that a property has changed, like so...

public object MyProperty
{
get { return _myProperty; }
set
{
_myProperty = value;
OnPropertyChanged("MyProperty");
}
}

Other objects can then subscribe to this event and will be notified every time the MyProperty property is changed.

Now, to answer your question as to whether the OnPropertyChanged method can be public. The answer is yes but you should be asking yourself why this would be the case.

Why would another class know when a property has changed so that it can call the method? if it already 'knows' when the property has changed then you shouldn't need to subscribe to the property changed event in the first place! Only the class itself should 'know' when one of its own properties has changed.

In your example You are notifying that the property 'sum' has been changed. but it hasn't. In fact, your code doesn't even allow that property to be changed outside of its own class.

I suspect that maybe you want some way of notifying that the sum property needs to be re-evaluated because a dependent property has been changed. If this is the case then you need to raise a property changed event when that dependent property changes.

Imagine that changes to the 'MyProperty' property shown earlier also means that 'Sum' has changed then that would be handled like this:

// This property is used by the 'sum' property so if this changes
// clients need to know that 'sum' has also changed.
public object MyProperty
{
get { return _myProperty; }
set
{
_myProperty = value;
OnPropertyChanged("MyProperty");
OnPropertyChanged("Sum");
}
}


Related Topics



Leave a reply



Submit