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
Get Property Value from String Using Reflection
How to Add User-Supplied Input to an SQL Statement
Send Values from One Form to Another Form
.Net String.Format() to Add Commas in Thousands Place For a Number
How to Convert Json Object to Custom C# Object
Using Cookiecontainer With Webclient Class
How to Enable External Request in Iis Express
Arraylist VS List≪≫ in C#
When Correctly Use Task.Run and When Just Async-Await
Programmatic Equivalent of Default(Type)
Cs0120: an Object Reference Is Required For the Nonstatic Field, Method, or Property 'Foo'
Mvvm: Tutorial from Start to Finish
Async/Await - When to Return a Task VS Void
How Would You Count Occurrences of a String (Actually a Char) Within a String
How to Provide User Name and Password When Connecting to a Network Share
Input String Was Not in a Correct Format
How to Convert Utf-8 Byte[] to String
How to Call Stored Procedure in Entity Framework 6 (Code-First)