Answer : Yes, There is.
The Interface INotifyPropertyChanged
is doing that, It is in the System.ComponentModel
namespace.
This interface contains an PropertyChanged
event, which will trigger when the property is changed.
C# 4 and below Example
public class DataCS4 : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
// Represent an Observable
private int firstNumber;
public int FirstNumber
{
get { return firstNumber; }
set
{
if (value == firstNumber)
return;
this.firstNumber = value;
OnPropertyChanged("FirstNumber");
OnPropertyChanged("Sum");
}
}
// Represent an Observable
private int secondNumber;
public int SecondNumber
{
get { return secondNumber; }
set
{
if (value == secondNumber)
return;
this.secondNumber = value;
OnPropertyChanged("SecondNumber");
OnPropertyChanged("Sum");
}
}
// Represent Computed
public int Sum { get { return FirstNumber + SecondNumber; } }
}
This is very simple example to Sum two integers
We have FirstNumber
property, (which we could consider as Knockout observable)
We have also SecondNumber
property (which we could consider as Knockout observable)
and We have Sum
property (which we could consider as Knockout Computed).
Now every time we change either FirstNumber
or SecondNumber
(by calling their corresponding set functions), we are notifying all subscribers for those properties (this done by calling the OnPropertyChanged
method)
Also we are calling the OnPropertyChanged
for the Sum
property to notify the subscribers for that property that the value of this property has changed.
NOTE This is very common pattern when using WPF + MVVM pattern.
When using C# 5.0 or C# 6.0, you could benefits from their new features to make the code little easier
C# 5.0 Example
public class DataCS5 : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
// Represent an Observable
private int firstNumber;
public int FirstNumber
{
get { return firstNumber; }
set
{
if (value == firstNumber)
return;
this.firstNumber = value;
OnPropertyChanged();
OnPropertyChanged("Sum");
}
}
// Represent an Observable
private int secondNumber;
public int SecondNumber
{
get { return secondNumber; }
set
{
if (value == secondNumber)
return;
this.secondNumber = value;
OnPropertyChanged();
OnPropertyChanged("Sum");
}
}
// Represent Computed
public int Sum { get { return FirstNumber + SecondNumber; } }
}
there is two changes here
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
, So by putting this attribute before the parameter of the method, The compiler will know the name of the property which called this method automatiilcay, without passing the property name manually, So we can write the following statement.
OnPropertyChanged();
which represent the second modification.
Note: CallerMemberName
attribute exists in the System.Runtime.CompilerServices
namespace
C# 6.0 Example
With C# 6.0 we could simplify the implemenation of the OnPropertyChanged
method.
public class DataCS6 : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
// C# 6.0
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
// Represent an Observable
private int firstNumber;
public int FirstNumber
{
get { return firstNumber; }
set
{
if (value == firstNumber)
return;
this.firstNumber = value;
OnPropertyChanged();
OnPropertyChanged("Sum");
}
}
// Represent an Observable
private int secondNumber;
public int SecondNumber
{
get { return secondNumber; }
set
{
if (value == secondNumber)
return;
this.secondNumber = value;
OnPropertyChanged();
OnPropertyChanged("Sum");
}
}
// Represent Computed
public int Sum => FirstNumber + SecondNumber;
}
Reference
Update
Responding to the following comment :
Interesting! But here you have to manage your dependencies yourself. You have to trigger a property changed for Sum. In knockout it figures out by itself which properties shall trigger a property changed on the computed. Any way of doing that?
I think you could you have two options (at least)
PropertyChanged library on GitHub.
The description of this library is :
Injects INotifyPropertyChanged code into properties at compile time
here is the new code after you download the library from the Nuget
[PropertyChanged.ImplementPropertyChanged]
public class Data
{
public int FirstNumber { get; set; }
public int SecondNumber { get; set; }
public int Sum => SecondNumber + FirstNumber;
}
2.PostSharp Library
Cheers !.