Don't Understand How INotifyPropertyChanged function - c#

INotifyPropertyChanged interface implementation (appropriate event) and helper method works fine in my code but I really don't understand how it works. My book does a poor job of explaining it to me in my opinion or I am not very smart.
We have a separate class Car.cs added to my solution with the following code (example from the book) and it should work in TwoWay Binding regards to TextBox control in my WPF application being changed when object's instance is being changed too:
public class Car: INotifyPropertyChanged
{
private string _make;
private string _model;
public event PropertyChangedEventHandler PropertyChanged;
public Car()
{
}
public string Make
{
get { return _make; }
set
{
if (_make != value)
{
_make = value;
OnPropertyChanged("Make");
}
}
}
public string Model
{
get { return _model; }
set
{
if(_model != value)
{
_model = value;
OnPropertyChanged("Model");
}
}
}
private void OnPropertyChanged (string propertyName)
{
if (PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}}
And here is the code which I made myself after learning Microsoft learning materials and it looks and works better imo:
public class Car: INotifyPropertyChanged
{
private string _make;
private string _model;
public event PropertyChangedEventHandler PropertyChanged;
public Car()
{
}
public string Make
{
get { return _make; }
set
{
_make = value;
OnPropertyChanged();
}
}
public string Model
{
get { return _model; }
set
{
_model = value;
OnPropertyChanged();
}
}
private void OnPropertyChanged ()
{
if (PropertyChanged != null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(""));
}
} }
So I have the foolowing question regards to example from the book (the first one):
What this part means? What's the point to check if property not equals to value (never saw anything like it before when during the Set part of the property you check that field of the class checked for being not equalled to value)? Code works even without it. No explanation for it in the book. Here is the code under question:
set
{
if (_make != value)
{
_make = value;
OnPropertyChanged("Make");
}
Why if you'll write the name of the property without brackets in the above-mentioned small piece of code (in subparagraph one) then it will not work? I meant if you'll write Make then it does not work and you should write "Make".
Thanks in advance!

Here is a basic example of how to correctly implement the IPropertyChanged interface, which notifies the view to re-draw on property changing its value.
public class PropertyChangedEventStackOverflow : INotifyPropertyChanged
{
//We have a private property of the same public property. This allows us to store the privous value of the property which we can
//get at any time when we ask.
string name = string.Empty;
//Public property is required to do two-way-binding or one-way-binding from model to the view.
public string Name
{
get => name;
set
{
if (name == value) //Check if the private property is already equals to the exact same value to avoid extra memory
return; //and uneccasary screen re-drawing. So we just return and avoid calling the OnPropertyChanged.
name = value; //If the value is different meaning we want to notify view and tell it to re-draw the screen for us.
OnPropertyChanged(nameof(Name));
}
}
//We create this helper metod to avoid doing something like this every single time we want to notify
string surname = string.Empty;
public string Surname
{
get => surname;
set
{
if (surname == value)
return;
surname = value;
PropertyChanged(this, new PropertyChangedEventArgs(nameof(Surname)));
}
}
//Here is the method to create for OnPropertyChangedEvent
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
var changed = PropertyChanged;
if (changed == null)
return;
changed.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
If you want to go step further and want to have an Action when your property has changed you can do something like this. backingStore type T = private property
public bool SetProperty<T>(ref T backingStore, T value,
[CallerMemberName] string propertyName = "",
Action onChanged = null)
{
if (EqualityComparer<T>.Default.Equals(backingStore, value))
return false;
backingStore = value;
onChanged?.Invoke();
OnPropertyChanged(propertyName);
return true;
}
You can give it an action to execute if once a property has changed.

Because raising the PropertyChanged event can be expensive in terms of performance (for example when a complex view has to redraw itself in order to display the latest data), it's a recommended pattern to raise it only when necessary.
It's only necessary when the new value has changed. To update the binding when the new value and the old value are the same is redundant and potentially expensive.
The following example is a very verbose version to highlight what's going on:
private string myProperty;
public string MyProperty
{
get => this.myProperty;
set
{
string newValue = value;
string oldValue = this.MyProperty;
if (newValue != oldValue)
{
this.myProperty = newValue;
OnPropertyChanged(nameof(this.MyProperty));
}
}
}
For reasons of performance, passing an empty string (string.Empty or "") or null to the PropertyChangedEventArgs instance, instead of the property name, will instruct the binding engine to update all bindings of the class that raises the PropertyChanged event.
In other words, it's like raising the PropertyChanged event for all properties at once.
That's why you only do this when this behavior is explicitly desired (for example when you reset and clear all properties):
/* Will raise the PropertyChanged event for ALL properties defined by the declaring type */
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(""));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(string.Empty));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
/* Will raise the PropertyChanged event ONLY for the particular property that matches the provided property name */
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("MyProperty"));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.MyProperty)));
"Make" is called a string literal. Every literal enclosed in double-quotes is interpreted as plain text by the compiler (string literal). If it wasn't the double-quotes, the compiler expects the value to be a language object (for example a variable or type).
If you check the signature of the constructor of PropertyChangedEventArgs you will see that it expects the property name as string.
Using Make (without the double-quotes) is a simple value reference (local or member variable) that returns the value of the Make property and not the name of the property (member name).
The property name is required to tell the binding engine which property has changed and therefore which Binding needs to be updated.
The following example assumes that the property Make is set to a value of "BMW":
public string Make
{
get => this.make;
set
{
// For example 'value' is "BMW"
this.make = value;
// Invocation a)
// Pass the property name as argument.
OnPropertyChanged("Make"); // Same as: OnPropertyChanged(nameof(this.Make))
// Invocation b)
// Pass the property value as argument
OnPropertyChanged(Make);
// Verbose version of b)
string modelMake = Make;
OnPropertyChanged(modelMake);
}
}
Version b) won't work, because the binding engine needs the property's member name and not the property value.
See Microsoft Docs: Strings and string literals to learn about strings.
See INotifyPropertyChanged to learn the recommended pattern to implement the interface
Remarks
Checking variable (for example an event delegate) for null using an if statement and the Null-conditional operator ?. or ?[] is redundant.
In high performance code you would want to avoid the redundant double checking.
The following versions are the same:
private void OnPropertyChanged()
{
if (PropertyChanged != null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
private void OnPropertyChanged()
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

What's the point to check if property not equals to value
it is just to avoid unnecessary updates. It is not strictly required. Whenever you have events to signal that something is updated it is often a good idea to check if the thing actually changed, since you do not know what is listening to the event, it could potentially trigger some slow computations.
Why if you'll write the name of the property without brackets in the above-mentioned small piece of code (in subparagraph one) then it will not work?
Because the event needs a string. You can let the compiler insert this string for you by writing OnPropertyChanged(nameof(Make)); You can also use CallerMemberName attribute to let the compiler insert this automatically, just call OnPropertyChanged() within your property.
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Going one step further you you can create a method that does both the comparison, field update, and event, letting you write a one line setter => Set(ref _make, value);
private void Set<T>(ref T field, T value, [CallerMemberName] string caller = "")
{
if (!EqualityComparer<T>.Default.Equals(field, value))
{
field = value;
OnPropertyChanged(caller);
}
}
However, my preference tend to be to wrap all of this inside a separate class:
public class Changeable<T> : INotifyPropertyChanged
{
private T currentValue;
public Changeable(T initialValue) => currentValue = initialValue;
public T Value
{
get => currentValue;
set
{
if (!EqualityComparer<T>.Default.Equals(currentValue, value))
{
this.currentValue = value;
OnPropertyChanged();
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
That way you can declare a property like public Changeable<string> Make {get;} = new (""); and bind to it like {Binding Make.Value}

INotifyPropertyChanged is an interface which only requires a class to implement it's requirements. Which is basically "just" an Event.
public interface INotifyPropertyChanged
{
//
// Summary:
// Occurs when a property value changes.
event PropertyChangedEventHandler? PropertyChanged;
}
The Method OnPropertyChanged is just a "helper" to raise this event. But the important part here, is that the INotifyPropertyChanged implementation by itself is not doing anything.
However WinForms and WPF are automatically subscribing in some higher-hirachy classes to such Event. They identify such classes through reflection and add themself to one of the subscribers to the PropertyChanged Event - which your class still needs to raise.
For this purpose you need to override the Setters of your properties:
public string Examle
{
get { return _field; }
set
{
_field = value;
OnPropertyChanged(); // <---
}
}
Otherwise the subscriber will never be notified. However the whole purpose of this is to decouple the handling of changes from the "Model" so your class which act's as container to the data - since the container can be passed to different forms, which might want to handle this container different.
The check if the value actually changed is only something you CAN do - to prevent the notification of the subscriber (let's say your data grid) to re-run all validations you've setup for such property. To avoid unnecessary runs of such validations this if will prevent notifying subscribers of such changes where a value get's re-assigned, but it remains the same.
The auto-wiring of the subscriber however is abstraced for you as developer, which can create difficulties for beginners to understand - however makes your life much more easier on the long run, as you can rely on the Framework as such (less for you to implement, test, etc.) so it's just a "convention" to use INotifyPropertyChange Interface, but the basic building blocks are general C# constructs.
Hope this helped a little :)

Related

How do I correctly implement INotifyPropertyChanged for my bool property and bind to CheckBox.IsChecked?

Novice here. I've been trying to wrap my head around databinding, and wanted to do try out two-way binding of a checkbox in the view to a boolean in a separate class that I've called "State". The point is to ensure that they are always in sync.
So I've made a checkbox in the view and bound it to the aforementioned boolean property in the State-class, accompanied by a button that bypasses the checkbox and toggles the boolean property directly (aptly labeled 'Ninja!'). The point was to test that the checkbox' databinding reacts when the property changes. However, I can't for the best of me figure out how the OnPropertyChanged-method is supposed to be invoked when the property changes.
Here's what I have so far:
<CheckBox x:Name="checkBox" Content="CheckBox" HorizontalAlignment="Left" Margin="232,109,0,0" VerticalAlignment="Top" IsChecked="{Binding Checked, Mode=TwoWay}"/>
<Button x:Name="button" Content="Ninja!" HorizontalAlignment="Left" Margin="228,182,0,0" VerticalAlignment="Top" Width="75" Click="button_Click"/>
And the code for the "State"-class I've made:
namespace TestTwoWayBinding
{
class State : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _checked;
public bool Checked {
get
{
return _checked;
}
set
{
_checked = value;
OnPropertyChanged(Checked);
}
}
public void Toggle()
{
if (!Checked)
{
Checked = true;
}
else
{
Checked = false;
}
}
public State(bool c)
{
this.Checked = c;
}
protected virtual void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(Checked));
}
}
}
}
And the code-behind on the view for initialization and handling the events:
namespace TestTwoWayBinding
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private State _state;
public MainWindow()
{
InitializeComponent();
_state = new State((bool)checkBox.IsChecked);
}
private void button_Click(object sender, RoutedEventArgs e)
{
_state.Toggle();
}
}
}
From what I gather, OnPropertyChanged expects a String propertyName, but I don't know what that would entail here. When I put in the name of the property (Checked), then that naturally refers to a boolean, not a string. What am I not getting? And what else am I doing wrong, as the checkbox doesn't register the property change when I change it through the button?
The two answers which suggest you pass the string literal "Checked" will work, but IMHO aren't the best way to do it. Instead, I prefer using [CallerMemberName] when implementing the OnPropertyChanged() method. (I have no idea what that third answer is all about…it doesn't appear to have anything to do with this question, and I'd guess it was just copy/pasted from somewhere else).
Here's an example of how I'd write your State class:
class State : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _checked;
public bool Checked
{
get { return _checked; }
set { _checked = value; OnPropertyChanged(); }
}
public void Toggle()
{
Checked = !Checked;
}
public State(bool c)
{
this.Checked = c;
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The key here is that the parameter marked with [CallerMemberName] will automatically be filled in with the correct value from the caller, simply by not passing any value. The default value of null is there just so the compiler will allow the caller to not pass a value.
Note that I also simplified the Toggle() method. There's no need to use an if statement to transform one bool value into another; that's what the Boolean operators are there for.
I also changed the OnPropertyChanged() method so that it's thread-safe, i.e. won't crash if some code unsubscribes the last handler from the PropertyChanged event between the time the event field is compared to null and the time the event is actually raised. Typically, this is a non-issue as these properties are nearly always accessed only from a single thread, but it's easy enough to protect against and is a good habit to get into.
Note that in C# 6, you have the option of just writing PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); for the method body. Not everyone is using the new compiler 100% of the time yet, so I just mention that as an optional choice for you.
Naturally, you also need to set the DataContext correctly, as shown in one of the other answers:
public MainWindow()
{
InitializeComponent();
_state = new State((bool)checkBox.IsChecked);
this.DataContext = _state;
}
Though, personally, I'm not sure I'd bother with the constructor. You appear to have no other code that would set checkBox.IsChecked, so it seems to me that you're always going to get the default value anyway. Besides, you can't create your view model class in XAML if it doesn't have a parameterized constructor; in the future, you may prefer to configure your DataContext like that. E.g.:
<Window.DataContext>
<l:State Checked="True"/>
</Window.DataContext>
And in the window's constructor:
public MainWindow()
{
InitializeComponent();
_state = (State)this.DataContext;
}
See also the related Q&A Automatically INotifyPropertyChanged. The question there is really about something different — they want to implement the interface without having to explicitly write anything in the property setter — but for better or worse, the answers they got are really more about your scenario, where it's just a question of simplifying the property setter implementation rather than making it completely automatic.
I have to admit, I would've thought there would have been another question already with which to mark yours as a duplicate. And I did find lots of related questions. But nothing that focuses directly on just "how do I implement and use a view model that implements INotifyPropertyChanged?", which is really what your question seems to be about.
Addendum:
I did some more searching, and while none of these seem like they would be considered exact duplicates per se, they all have good information that help address the question about implementing INotifyPropertyChanged:
Use of Attributes… INotifyPropertyChanged
INotifyPropertyChanged for model and viewmodel
BindableBase vs INotifyChanged
How to write “ViewModelBase” in MVVM (WPF)
You are real close. You need to make 2 small changes and your test works:
Assign the DataContext of your Window to the _state variable.
Put the string "Checked" into the OnPropertyChanged and pass propertyName to the PropertyChangedEventArgs in the OnPropertyChanged method.
So your MainWindow ctor becomes:
public MainWindow()
{
InitializeComponent();
_state = new State((bool)checkBox.IsChecked);
this.DataContext = _state;
}
and the State class file looks like:
class State : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _checked;
public bool Checked
{
get
{
return _checked;
}
set
{
_checked = value;
OnPropertyChanged("Checked");
}
}
public void Toggle()
{
if (!Checked)
{
Checked = true;
}
else
{
Checked = false;
}
}
public State(bool c)
{
this.Checked = c;
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
As a novice, I recommend you learn more about Model-View-ViewModel MVVM Design Pattern. It is a common pattern with WPF and helps encourage separation of concerns (keeping your business logic out of your user interface logic)
The OnPropertyChanged method expects the Checked property's name as argument - at the moment, you're passing its value!
This means, change the Checked property declaration to:
public bool Checked {
get
{
return _checked;
}
set
{
_checked = value;
OnPropertyChanged("Checked");
}
}

OnPropertyChanged parameter of INotifyPropertyChanged

I am getting into MVVM and stumbled upon two versions of calling the OnPropertyChanged function shown in this MWE:
public class SampleModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get { return _name; }
set
{
if (value == _name) return;
_name = value;
// Version 1
OnPropertyChanged();
// Version 2
OnPropertyChanged(nameof(Name));
}
}
#region INotifyPropertyChanged members
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Why would I chose version 1 over version 2 and vice-versa?
The OnPropertyChanged(); call will automatically get the name of the classmember that called it, due to the [CallerMemberName] attribute.
The OnPropertyChanged(nameof(Name)); explicitly sends the property name as input parameter to the method.
So in your case the two calls would result in the exact same PropertyChanged invocation.
But the explicit call is useful if you change _name in a method or something like that, and want subscribers to be notified.
Version 2 (with an explicit property name) is useful if the value of the property changes without calling the setter (as a side effect of some other operation) and you want to notify of that. In that case, the implicit CallerMemberName would not provide the correct property name.
This mainly comes up if you have a get-only property that does some sort of transformation of internal state to provide feedback. Every time that internal state changes, you notify that the get-only property (may) have changed. Personally I try to avoid this pattern, as it's easy to forget to notify when you make changes to the code later.

Raise an event in main whenever a variable's value changed in a class in c#?

I have a changed event in a class, which is triggered at a variable's value change but I dont know how to send a notification to the main of my C# windows form, for example, show a message box to notify the value changed. I dont wanna set a timer to check every second for a response. There is any better way?
You are probably looking for INotifyPropertyChanged, a common pattern in WPF but it can also be used for Windows Forms. There is a pretty large example on the documentation page:
https://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged(v=vs.110).aspx
class SomeClass : INotifyPropertyChanged
{
private int foo;
public event PropertyChangedEventHandler PropertyChanged;
public int Foo
{
get { return foo; }
set
{
if (foo == value)
return;
foo = value;
PropertyChanged("Foo");
}
}
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Please note that much of this becomes safer and much less boilerplate you use newer .NET features such as [CallerMemberName]
As other posted you should impement INotifyPropertyChanged, but in addition I would suggest the following as well:
In the setter of your property check whether the value, that is assigned to your property really changes the property's value. Otherwise you probably fire the notify change to often.
You should also implement ISupportInitialize. Usually, you fire the property changed event to inform your main form that data has changed and needs to be saved. But if you load your instances from e.g. a database, the property changed event would also fire, indicating data has changed, which is not true in that sense. With ISupportInitialize you can tell your instances that they are being initialized and that they should not fire the notify property changed event.
Here is some sample code:
class MyTest : INotifyPropertyChanged, ISupportInitialize
{
public event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged;
public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);
private bool _IsInitializing;
private bool _MyProperty;
public void BeginInit()
{
_IsInitializing = true;
}
public void EndInit()
{
_IsInitializing = false;
}
public bool MyProperty {
get { return _MyProperty; }
set {
if (_MyProperty == value)
return;
_MyProperty = value;
OnPropertyChanged("MyProperty");
}
}
private void OnPropertyChanged(string propertyName)
{
if (_IsInitializing)
return;
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
For initialization you would write (to prevent the property changed event from firering):
MyTest thisTest = new MyTest();
thisTest.BeginInit();
thisTest.MyProperty = true;
thisTest.EndInit();
You should wrap your variable with a "Property" (Getter and Setter).
Then you can implement in the "Setter" either your own event, or you can trigger the property changed event from WPF (if you are using WPF).

Trigger an event when something changes in a class

Is it possible to trigger some event when something changes in a given class?
E.g. I have a class that has 100 fields and one of them is being modified externally or internally. Now I want to catch this event. How to do this?
The most I wonder if there is a trick to do this quickly for really extended classes.
As a best practice, convert your public fields to manual properties and implement your class with the INotifyPropertyChanged interface in order to raise a change event.
EDIT: Because you mentioned 100 fields I would suggest you to refactor your code like in this great answer: Tools for refactoring C# public fields into properties
Here is an example of it:
private string _customerNameValue = String.Empty;
public string CustomerName
{
get
{
return this._customerNameValue;
}
set
{
if (value != this._customerNameValue)
{
this._customerNameValue = value;
NotifyPropertyChanged();
}
}
}
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Check this out: INotifyPropertyChanged Interface

Is there a way to find out if a property or variable has been set?

As per title, can I find out the history of a property or variable, in terms of whether it has been set or not?
Reason for this is I have some query classes, any properties that have not been set should not be included in the generated query.
Currently I am just trying to add a PropertyInfo instance (I need the name of the property, this is vital to the query generation due to mappings) to a list of properties which have been set in set{}. This is ugly though as it requires more code for what should be classes that only contain properties and no logic (i.e. no removal of stufffrom a list).
Is there something built-in I can use or a more elegant method?
Assuming you're talking about dynamic entities, you could check for null using reflection but this won't tell you anything if the original value was null (or zero in case of numeric data-types).
But best way is to make it implement INotifyPropertyChanged and have a list of properties that have been materialized.
public class Item : INotifyPropertyChanged
{
private List<string> _MaterializedPropertiesInternal;
private List<string> MaterializedPropertiesInternal
{
get
{
if (_MaterializedPropertiesInternal==null)
_MaterializedPropertiesInternal = new List<string>();
return _MaterializedPropertiesInternal;
}
}
private ReadOnlyCollection<string> _MaterializedProperties;
public IEnumerable<string> MaterializedProperties
{
get
{
if (_MaterializedProperties==null) _MaterializedProperties =
new ReadOnlyCollection<string>(MaterializedPropertiesInternal);
return _MaterializedProperties;
}
}
private int _MyProperty;
public int MyProperty
{
get { return _MyProperty; }
set
{
_MyProperty = value;
OnPropertyChanged("MyProperty");
}
}
protected void OnPropertyChanged(string propertyName)
{
if(PropertyChanged != null) PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
MaterializedPropertiesInternal.Add(propertyName);
}
public event PropertyChangedEventHandler PropertyChanged;
}
Make the properties nullable, e.g. of type int? instead of type int, so that their value is null if they haven't been set.

Categories

Resources