276 lines
6.6 KiB
C#
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
|
|
}
|
|
}
|