How Does Wpf Inotifypropertychanged Work

How does WPF INotifyPropertyChanged work?

How does this call activates ? What's the C#'s magic behind
this to make it possible?

This code creates a Binding object which links the TextBlock's Text property to the ViewModel property. It also adds an event handler to the ViewModel's PropertyChanged event to update the text value when the ViewModel fires the PropertyChanged event (with the right property).

Why is if (PropertyChanged != null) necessary? Who sets up the
PropertyChanged to what value?

If the PropertyChanged event is null, then firing it will cause a NullReferenceException.

The meaning of Mode=TwoWay looks like that it not only can signal the
change, but also updates the content when other Binding element with
the same name in binding changes, then what about OneWay mode? Can we
set a Binding as source only or target only?

The binding modes are:

  • TwoWay: Changes the bound value when the ViewModel property changes and vice versa
  • OneWay: Changes the bound value when the ViewModel property changes only
  • OneWayToSource: Changes the ViewModel property when the bound value changes only
  • OneTime: Sets the bound value to the value of the ViewModel property just when the application is created or the data context changes.

You can read more about them here: http://msdn.microsoft.com/en-us/library/system.windows.data.bindingmode.aspx

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));
}

Understanding how PropertyChanged mechanism works (workflow)

I recommend you look up Events and Delegates in C# for further reading.

public event PropertyChangedEventHandler PropertyChanged;

PropertyChanged ist an EventHandler, which is a delegate. Other code can register here and get executed when the delegate is called.

So what happens with INotifyPropertyChanged is:

Some Code (propably the binding in Xaml) registers for the PropertyChanged event:

yourobject.PropertyChanged += MethodThatShouldBeCalledWhenThePropertyChanges;

(This is most properly auto generated somewhere, because it happens from xaml, but you could as well to it this way by hand.)

In the NotifyPropertyChanged Method, the event delegate gets executed.
It simply executes all methods that were added to the event.

So to answer your questions:

Yes, the code in NotifyPropertyChanged "triggers" the event. It calls every method that was added to the event.

Any code can register for events.

As of your Update:

I again recommend reading into delegates.

You can think of delegates as Method Interface. It defines that a method has to take specific parameter types to match the delegate.

PropertyChanged is of type PropertyChangedEventHandler which takes an object and a PropertyChangedEventArgs parameter.

So any method like this is suitable:

void MethodName(
Object sender,
PropertyChangedEventArgs e
)

How does WPF know to use INotifyPropertyChanged when I bind to IEnumerable?

Even though your Data property is returned with the type of IEnumerable<IData>, the object itself is still a ObservableCollection<ObservableData>. WPF can just use the is or as operators to test whether any particular object implements INotifyPropertyChanged, regardless of the handle provided.

IEnumerable<IData> test = Data;
if (test is INotifyPropertyChanged) {
//This if block enters because test is really ObservableCollection<ObservableData>
INotifyPropertyChanged test2 = (INotifyPropertyChanged)test;
}

Understanding INotifyPropertyChanged

The INotifyPropertyChanged interface is used to notify clients, typically binding clients, that a property value has changed.

OnPropertyChanged("NameFromClassA") is typically implemented in the BaseViewModel Class and is equivalent to doing :

    if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs("NameFromClassA"));

It tells whoever is listening to the PropertyChanged event of your Demo Class that a Property named "NameFromClassA" has changed value (your example is wrong because there's no such Property in your Class).

It's required for DependencyProperties bound to Properties of your VM to update themselves and is rarely used for anything else.


Edit:

Psoeudo-code of what's roughly equivalent to what the binding engine does behind the scene :

ClassA myClassA;
string myPropertyBoundToNameOfClassA;

// somewhere after myClassA was initialized
myClassA.PropertyChanged += OnMyClassAPropertyChanged;

void MyClassAPropertyChanged(string name)
{
if (name == "NameFromClassA")
myPropertyBoundToNameOfClassA = myClassA.Name;
}

Why use INotifyPropertyChanged with bindings in WPF?

You don't need INotifyPropertyChanged if you only intend to use the binding to write to the property (as you have found out), but you do need it so that you can tell that someone else wrote to the property and update the displayed value accordingly.

To see what I 'm talking about, add a button to your window that when clicked directly changes the value of the bound property (not the corresponding attribute of the UI element bound to that property). With INotifyPropertyChanged, you will see the UI updating itself to the new value when you click the button; without it, the UI will still show the "old" value.

How INotifyPropertyChanged's PropertyChanged event get assigned?

The WPF data-binding infrastructure will add a PropertyChanged handler when you set the object as a DataContext, in order to detect changes to your properties.

You can watch this happen by setting a breakpoint.

The OnPropertyChanged method that it points to is an internal WPF method, as you can see by inspecting the Target property of the delegate.

Binding works without INotifyPropertyChanged, why?

[...] you are encountering a
another hidden aspect of WPF, that's it WPF's data binding engine will
data bind to PropertyDescriptor instance which wraps the source
property if the source object is a plain CLR object and doesn't
implement INotifyPropertyChanged interface. And the data binding
engine will try to subscribe to the property changed event through
PropertyDescriptor.AddValueChanged() method. And when the target data
bound element change the property values, data binding engine will
call PropertyDescriptor.SetValue() method to transfer the changed
value back to the source property, and it will simultaneously raise
ValueChanged event to notify other subscribers (in this instance, the
other subscribers will be the TextBlocks within the ListBox.

And if you are implementing INotifyPropertyChanged, you are fully
responsible to implement the change notification in every setter of
the properties which needs to be data bound to the UI. Otherwise, the
change will be not synchronized as you'd expect.

See: Data binding without INotifyPropertyChanged

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);
}


Related Topics



Leave a reply



Submit