using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; namespace AllBase { /// /// 提供属性更改通知功能的基类 /// public abstract class BindableBase : INotifyPropertyChanged { public event PropertyChangedEventHandler? PropertyChanged; private Dictionary? _propertyBag; #region PropertyBag Accessor private Dictionary PropertyBag => _propertyBag ??= []; #endregion #region Constructor & Initialization public BindableBase() { OnInitialized(); } public virtual void OnInitialized() { } #endregion #region Property Name Extraction public static string GetPropertyName(Expression> expression) { return GetPropertyNameFast(expression); } internal static string GetPropertyNameFast(LambdaExpression expression) { if (expression.Body is not MemberExpression memberExpression) { throw new ArgumentException("MemberExpression is expected in expression.Body", nameof(expression)); } MemberInfo member = memberExpression.Member; if (member.MemberType == MemberTypes.Field && member.Name != null && member.Name.StartsWith("$VB$Local_")) { return member.Name.Substring("$VB$Local_".Length); } string result = member.Name ?? throw new ArgumentNullException(nameof(member.Name), "member.Name is null"); return result; } #endregion #region Property Access (Expression-based) protected T? GetProperty(Expression> expression) { return GetPropertyCore(GetPropertyName(expression)); } protected bool SetProperty(Expression> expression, T value) { return SetProperty(expression, value, (Action?)null); } protected bool SetProperty(Expression> expression, T value, Action? changedCallback) { string propertyName = GetPropertyName(expression); return SetPropertyCore(propertyName, value, changedCallback); } protected bool SetProperty(Expression> expression, T value, Action? changedCallback) { string propertyName = GetPropertyName(expression); return SetPropertyCore(propertyName, value, changedCallback); } protected bool SetProperty(ref T storage, T value, Expression> expression) { return SetProperty(ref storage, value, expression, null); } protected bool SetProperty(ref T storage, T value, Expression> expression, Action? changedCallback) { return SetProperty(ref storage, value, GetPropertyName(expression), changedCallback); } protected void RaisePropertyChanged(Expression> expression) { RaisePropertyChanged(GetPropertyName(expression)); } #endregion #region Property Access (CallerMemberName-based) protected T? GetValue([CallerMemberName] string? propertyName = null) { GuardPropertyName(propertyName); return GetPropertyCore(propertyName); } protected bool SetValue(T value, [CallerMemberName] string? propertyName = null) { return SetValue(value, (Action?)null, propertyName); } protected bool SetValue(T value, Action? changedCallback, [CallerMemberName] string? propertyName = null) { return SetPropertyCore(propertyName!, value, changedCallback); } protected bool SetValue(T value, Action? changedCallback, [CallerMemberName] string? propertyName = null) { return SetPropertyCore(propertyName!, value, changedCallback); } protected bool SetValue(ref T storage, T value, [CallerMemberName] string? propertyName = null) { return SetValue(ref storage, value, null, propertyName); } protected bool SetValue(ref T storage, T value, Action? changedCallback, [CallerMemberName] string? propertyName = null) { GuardPropertyName(propertyName); return SetProperty(ref storage, value, propertyName, changedCallback); } #endregion #region Property Access (String-based) protected bool SetProperty(ref T storage, T value, string propertyName) { return SetProperty(ref storage, value, propertyName, null); } protected virtual bool SetProperty(ref T storage, T value, string propertyName, Action? changedCallback) { VerifyAccess(); if (CompareValues(storage, value, propertyName)) { return false; } storage = value; RaisePropertyChanged(propertyName); changedCallback?.Invoke(); return true; } #endregion #region PropertyBag Core Access private T? GetPropertyCore(string propertyName) { if (PropertyBag.TryGetValue(propertyName, out object? value)) { return (T?)value; } return default; } protected virtual bool SetPropertyCore(string propertyName, T value, out T? oldValue) { VerifyAccess(); oldValue = default; if (PropertyBag.TryGetValue(propertyName, out object? value2)) { oldValue = (T?)value2; } if (CompareValues(oldValue, value, propertyName)) { return false; } lock (PropertyBag) { PropertyBag[propertyName] = value; } RaisePropertyChanged(propertyName); return true; } private bool SetPropertyCore(string propertyName, T value, Action? changedCallback) { bool flag = SetPropertyCore(propertyName, value, out T? oldValue); if (flag) { changedCallback?.Invoke(); } return flag; } private bool SetPropertyCore(string propertyName, T value, Action? changedCallback) { bool flag = SetPropertyCore(propertyName, value, out T? oldValue); if (flag) { changedCallback?.Invoke(oldValue); } return flag; } #endregion #region PropertyChanged Notification protected virtual void RaisePropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } protected void RaisePropertyChanged() { RaisePropertiesChanged(null); } protected void RaisePropertiesChanged(params string[]? propertyNames) { if (propertyNames == null || propertyNames.Length == 0) { RaisePropertyChanged(string.Empty); return; } foreach (string propertyName in propertyNames) { RaisePropertyChanged(propertyName); } } #endregion #region Validation & Utility #pragma warning disable CS8777 private static void GuardPropertyName(string? propertyName) { if (string.IsNullOrEmpty(propertyName)) { throw new ArgumentNullException(nameof(propertyName)); } } #pragma warning restore CS8777 protected virtual void VerifyAccess() { } protected virtual bool CompareValues(T storage, T value, string propertyName) { return EqualityComparer.Default.Equals(storage, value); } #endregion } }