PropertyChangedProxy - Houzkin/TreeStructures GitHub Wiki
The purpose is to assist in implementing INotifyPropertyChanged.
/// <summary>
/// A proxy class that supports the implementation of <see cref="INotifyPropertyChanged"/>.
/// It helps manage property changes and notifications efficiently.
///</summary>
public class PropertyChangeProxy {
/// <summary>
/// Initializes a new instance of the <see cref="PropertyChangeProxy"/> class
/// that delegates notifications to the specified event handler.
/// </summary>
/// <param name="sender">The object that owns this proxy.</param>
/// <param name="handlerGetter">A function that retrieves the event handler for notifications.</param>
public PropertyChangeProxy(object? sender, Func<PropertyChangedEventHandler?> handlerGetter) { }
/// <summary>
/// Initializes a new instance of the <see cref="PropertyChangeProxy"/> class
/// that delegates notifications using a specified action.
/// </summary>
/// <param name="raiseAction">An action to invoke property change notifications.</param>
public PropertyChangeProxy(Action<PropertyChangedEventArgs> raiseAction) { }
/// <summary>
/// Sets the given storage value and sends a property change notification if the value has changed.
/// </summary>
/// <typeparam name="T">The type of the property.</typeparam>
/// <param name="storage">A reference to the backing field of the property.</param>
/// <param name="value">The new value to set.</param>
/// <param name="propertyName">The name of the property (automatically determined).</param>
/// <returns>A result indicating whether the value was changed.</returns>
public ResultWithValue<PropertyChangeProxy> TrySetAndNotify<T>(ref T storage, T value, [CallerMemberName] string? propertyName = null) { }
/// <summary>
/// Sends a property change notification for the specified property name.
/// </summary>
/// <param name="propertyName">The name of the property (automatically determined).</param>
/// <returns>The current instance of <see cref="PropertyChangeProxy"/>.</returns>
public PropertyChangeProxy Notify([CallerMemberName] string? propertyName = null) { }
/// <summary>
/// Retrieves a cached <see cref="PropertyChangedEventArgs"/> instance for the specified property name.
/// If no cached instance exists, a new one is created and stored.
/// </summary>
/// <param name="name">The name of the property for which to retrieve an event args instance.</param>
/// <returns>A cached <see cref="PropertyChangedEventArgs"/> instance associated with the given property name.</returns>
public static PropertyChangedEventArgs GetOrAddCachedEventArgs(string name) { }
}This class defines two constructors.
Use the first constructor for simplicity if you don't plan to customize the behavior throgh inheritance.
Use the second constructor to maintain extensibility if you intend to cutomize behavior by overriding members.
public class NotifierBase : INotifyPropertyChanged {
public NotiferBase () {
PropertyChangeNotifier = new PropertyChangeProxy(this, () => PropertyChanged);
}
protected readonly PropertyChangeProxy PropertyChangeNotifier;
public event PropertyChangedEventHandler? PropertyChanged;
}
public class NotifierPerson : NotifierBase {
string _firstName = "";
string _lastName = "";
public string FirstName {
get => _fistName;
set => PropertyChangeNotifier.TrySetAndNotify(ref _firstName, value).When(o => o.Notify(nameof(FullName)));
}
public string LastName {
get => _lastName;
set => PropertyChangeNotifier.TrySetAndNotify(ref _lastName, value).When(o => o.Notify(nameof(FullName)));
}
public string FullName => FistName + " " + LastName;
}public class NotifierBase : INotifyPropertyChanged {
public NotifierBase() {
propChangeNotifier = new PropertyChangeProxy(RaisePropertyChangedEvent);
}
private readonly PropertyChangeProxy propChangeNotifier;
protected virtual void RaisePropertyChangedEvent(PropertyChangedEventArgs e) {
PropertyChanged?.Invoke(this, e);
}
public virtual event PropertyChangedEventHandler? PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName]string? propName = null)
=> propChangeNotifier.TrySetAndNotify(ref storage, value, propName);
protected void OnPropertyChanged([CallerMemberName]string? propName = null)
=> propChangeNotifier.Notify(propName);
}
public class NotifierPerson : NotifierBase {
string _firstName = "";
string _lastName = "";
public string FirstName {
get => _fistName;
set {
if (SetProperty(ref _firstName, value)) OnPropertyChanged(nameof(FullName));
}
}
public string LastName {
get => _lastName;
set {
if (SetProperty(ref _lastName, value)) OnPropertyChanged(nameof(FullName));
}
}
public string FullName => FirstName + " " + LastName;
// override members
protected override void RaisePropertyChangedEvent(PropertyChangedEventArgs e) {
Console.WriteLine("Raise Event.");
base.RaisePropertyChangedEvent(e);
}
public override event PropertyChangedEventHandler? PropertyChanged {
add {
base.PropertyChanged += value;
Console.WriteLine("Handler added.");
}
remove {
base.PropertyChanged -= value;
Console.WriteLine("Handler removed.");
}
}
}- Enable simple and concise code, as shown in the example above.
- Minimize the cost of initializing new
PropertyChangedEventArgsinstances by caching them in static fields keyed by property name. - Ensures thread safety by using a
ConcurrentDictionaryto store the cache.