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