Files
KJD/AllBase/BindableBase.cs
2026-09-07 13:49:38 +08:00

276 lines
6.6 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace AllBase
{
/// <summary>
/// 提供属性更改通知功能的基类
/// </summary>
public abstract class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
private Dictionary<string, object?>? _propertyBag;
#region PropertyBag Accessor
private Dictionary<string, object?> PropertyBag => _propertyBag ??= [];
#endregion
#region Constructor & Initialization
public BindableBase()
{
OnInitialized();
}
public virtual void OnInitialized()
{
}
#endregion
#region Property Name Extraction
public static string GetPropertyName<T>(Expression<Func<T>> 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<T>(Expression<Func<T>> expression)
{
return GetPropertyCore<T>(GetPropertyName(expression));
}
protected bool SetProperty<T>(Expression<Func<T>> expression, T value)
{
return SetProperty(expression, value, (Action?)null);
}
protected bool SetProperty<T>(Expression<Func<T>> expression, T value, Action? changedCallback)
{
string propertyName = GetPropertyName(expression);
return SetPropertyCore(propertyName, value, changedCallback);
}
protected bool SetProperty<T>(Expression<Func<T>> expression, T value, Action<T?>? changedCallback)
{
string propertyName = GetPropertyName(expression);
return SetPropertyCore(propertyName, value, changedCallback);
}
protected bool SetProperty<T>(ref T storage, T value, Expression<Func<T>> expression)
{
return SetProperty(ref storage, value, expression, null);
}
protected bool SetProperty<T>(ref T storage, T value, Expression<Func<T>> expression, Action? changedCallback)
{
return SetProperty(ref storage, value, GetPropertyName(expression), changedCallback);
}
protected void RaisePropertyChanged<T>(Expression<Func<T>> expression)
{
RaisePropertyChanged(GetPropertyName(expression));
}
#endregion
#region Property Access (CallerMemberName-based)
protected T? GetValue<T>([CallerMemberName] string? propertyName = null)
{
GuardPropertyName(propertyName);
return GetPropertyCore<T>(propertyName);
}
protected bool SetValue<T>(T value, [CallerMemberName] string? propertyName = null)
{
return SetValue(value, (Action?)null, propertyName);
}
protected bool SetValue<T>(T value, Action? changedCallback, [CallerMemberName] string? propertyName = null)
{
return SetPropertyCore(propertyName!, value, changedCallback);
}
protected bool SetValue<T>(T value, Action<T?>? changedCallback, [CallerMemberName] string? propertyName = null)
{
return SetPropertyCore(propertyName!, value, changedCallback);
}
protected bool SetValue<T>(ref T storage, T value, [CallerMemberName] string? propertyName = null)
{
return SetValue(ref storage, value, null, propertyName);
}
protected bool SetValue<T>(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<T>(ref T storage, T value, string propertyName)
{
return SetProperty(ref storage, value, propertyName, null);
}
protected virtual bool SetProperty<T>(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<T>(string propertyName)
{
if (PropertyBag.TryGetValue(propertyName, out object? value))
{
return (T?)value;
}
return default;
}
protected virtual bool SetPropertyCore<T>(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<T>(string propertyName, T value, Action? changedCallback)
{
bool flag = SetPropertyCore(propertyName, value, out T? oldValue);
if (flag)
{
changedCallback?.Invoke();
}
return flag;
}
private bool SetPropertyCore<T>(string propertyName, T value, Action<T?>? 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>(T storage, T value, string propertyName)
{
return EqualityComparer<T>.Default.Equals(storage, value);
}
#endregion
}
}