using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.ExceptionServices; using Avalonia.Utilities; namespace Avalonia.Data.Core.Plugins { /// /// Reads a property from a . /// public class AvaloniaPropertyAccessorPlugin : IPropertyAccessorPlugin { /// [RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)] public bool Match(object obj, string propertyName) { if (obj is AvaloniaObject o) { return LookupProperty(o, propertyName) != null; } return false; } /// /// Starts monitoring the value of a property on an object. /// /// A weak reference to the object. /// The property name. /// /// An interface through which future interactions with the /// property will be made. /// [RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)] public IPropertyAccessor? Start(WeakReference reference, string propertyName) { _ = reference ?? throw new ArgumentNullException(nameof(reference)); _ = propertyName ?? throw new ArgumentNullException(nameof(propertyName)); if (!reference.TryGetTarget(out var instance) || instance is null) return null; var o = (AvaloniaObject)instance; var p = LookupProperty(o, propertyName); if (p != null) { return new Accessor(new WeakReference(o), p); } else if (instance != AvaloniaProperty.UnsetValue) { var message = $"Could not find AvaloniaProperty '{propertyName}' on '{instance}'"; var exception = new MissingMemberException(message); return new PropertyError(new BindingNotification(exception, BindingErrorType.Error)); } else { return null; } } private static AvaloniaProperty? LookupProperty(AvaloniaObject o, string propertyName) { return AvaloniaPropertyRegistry.Instance.FindRegistered(o, propertyName); } private class Accessor : PropertyAccessorBase, IWeakEventSubscriber { private readonly WeakReference _reference; private readonly AvaloniaProperty _property; public Accessor(WeakReference reference, AvaloniaProperty property) { _reference = reference ?? throw new ArgumentNullException(nameof(reference)); _property = property ?? throw new ArgumentNullException(nameof(property)); } public AvaloniaObject? Instance { get { _reference.TryGetTarget(out var result); return result; } } public override Type? PropertyType => _property?.PropertyType; public override object? Value => Instance?.GetValue(_property); public override bool SetValue(object? value, BindingPriority priority) { if (!_property.IsReadOnly) { Instance?.SetValue(_property, value, priority); return true; } return false; } void IWeakEventSubscriber. OnEvent(object? notifyPropertyChanged, WeakEvent ev, AvaloniaPropertyChangedEventArgs e) { if (e.Property == _property) { SendCurrentValue(); } } protected override void SubscribeCore() { SubscribeToChanges(); SendCurrentValue(); } protected override void UnsubscribeCore() { var instance = Instance; if (instance != null) WeakEvents.AvaloniaPropertyChanged.Unsubscribe(instance, this); } private void SendCurrentValue() { try { var value = Value; PublishValue(value); } catch { } } private void SubscribeToChanges() { var instance = Instance; if (instance != null) WeakEvents.AvaloniaPropertyChanged.Subscribe(instance, this); } } } }