diff --git a/src/Avalonia.Base/Utilities/WeakObservable.cs b/src/Avalonia.Base/Utilities/WeakObservable.cs index c261cc0520..b1fe78c206 100644 --- a/src/Avalonia.Base/Utilities/WeakObservable.cs +++ b/src/Avalonia.Base/Utilities/WeakObservable.cs @@ -16,12 +16,13 @@ namespace Avalonia.Utilities /// Converts a .NET event conforming to the standard .NET event pattern into an observable /// sequence, subscribing weakly. /// + /// The type of target. /// The type of the event args. /// Object instance that exposes the event to convert. /// Name of the event to convert. /// - public static IObservable> FromEventPattern( - object target, + public static IObservable> FromEventPattern( + TTarget target, string eventName) where TEventArgs : EventArgs { diff --git a/src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs b/src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs index 356be2ba31..f1e8fa6f9b 100644 --- a/src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs +++ b/src/Avalonia.Base/Utilities/WeakSubscriptionManager.cs @@ -17,22 +17,23 @@ namespace Avalonia.Utilities /// /// Subscribes to an event on an object using a weak subscription. /// - /// The type of the event arguments. + /// The type of the target. + /// The type of the event arguments. /// The event source. /// The name of the event. /// The subscriber. - public static void Subscribe(object target, string eventName, IWeakSubscriber subscriber) - where T : EventArgs + public static void Subscribe(TTarget target, string eventName, IWeakSubscriber subscriber) + where TEventArgs : EventArgs { - var dic = SubscriptionTypeStorage.Subscribers.GetOrCreateValue(target); - Subscription sub; + var dic = SubscriptionTypeStorage.Subscribers.GetOrCreateValue(target); + Subscription sub; if (!dic.TryGetValue(eventName, out sub)) { - dic[eventName] = sub = new Subscription(dic, target, eventName); + dic[eventName] = sub = new Subscription(dic, typeof(TTarget), target, eventName); } - sub.Add(new WeakReference>(subscriber)); + sub.Add(new WeakReference>(subscriber)); } /// @@ -84,19 +85,18 @@ namespace Avalonia.Utilities private WeakReference>[] _data = new WeakReference>[16]; private int _count = 0; - public Subscription(SubscriptionDic sdic, object target, string eventName) + public Subscription(SubscriptionDic sdic, Type targetType, object target, string eventName) { _sdic = sdic; _target = target; _eventName = eventName; - var t = target.GetType(); Dictionary evDic; - if (!Accessors.TryGetValue(t, out evDic)) - Accessors[t] = evDic = new Dictionary(); + if (!Accessors.TryGetValue(targetType, out evDic)) + Accessors[targetType] = evDic = new Dictionary(); if (!evDic.TryGetValue(eventName, out _info)) { - var ev = t.GetRuntimeEvents().FirstOrDefault(x => x.Name == eventName); + var ev = targetType.GetRuntimeEvents().FirstOrDefault(x => x.Name == eventName); if (ev == null) { diff --git a/src/Markup/Avalonia.Markup/Data/IndexerNode.cs b/src/Markup/Avalonia.Markup/Data/IndexerNode.cs index 8ab665c4a4..4e2914a148 100644 --- a/src/Markup/Avalonia.Markup/Data/IndexerNode.cs +++ b/src/Markup/Avalonia.Markup/Data/IndexerNode.cs @@ -33,8 +33,8 @@ namespace Avalonia.Markup.Data if (incc != null) { - inputs.Add(WeakObservable.FromEventPattern( - target, + inputs.Add(WeakObservable.FromEventPattern( + incc, nameof(incc.CollectionChanged)) .Where(x => ShouldUpdate(x.Sender, x.EventArgs)) .Select(_ => GetValue(target))); @@ -42,8 +42,8 @@ namespace Avalonia.Markup.Data if (inpc != null) { - inputs.Add(WeakObservable.FromEventPattern( - target, + inputs.Add(WeakObservable.FromEventPattern( + inpc, nameof(inpc.PropertyChanged)) .Where(x => ShouldUpdate(x.Sender, x.EventArgs)) .Select(_ => GetValue(target))); diff --git a/src/Markup/Avalonia.Markup/Data/Plugins/AvaloniaPropertyAccessorPlugin.cs b/src/Markup/Avalonia.Markup/Data/Plugins/AvaloniaPropertyAccessorPlugin.cs index 1d4a9a688e..3f6f15ed5b 100644 --- a/src/Markup/Avalonia.Markup/Data/Plugins/AvaloniaPropertyAccessorPlugin.cs +++ b/src/Markup/Avalonia.Markup/Data/Plugins/AvaloniaPropertyAccessorPlugin.cs @@ -13,7 +13,15 @@ namespace Avalonia.Markup.Data.Plugins public class AvaloniaPropertyAccessorPlugin : IPropertyAccessorPlugin { /// - public bool Match(WeakReference reference) => reference.Target is AvaloniaObject; + public bool Match(object obj, string propertyName) + { + if (obj is AvaloniaObject a) + { + return AvaloniaPropertyRegistry.Instance.FindRegistered(a, propertyName) != null; + } + + return false; + } /// /// Starts monitoring the value of a property on an object. diff --git a/src/Markup/Avalonia.Markup/Data/Plugins/IPropertyAccessorPlugin.cs b/src/Markup/Avalonia.Markup/Data/Plugins/IPropertyAccessorPlugin.cs index 187142bd0e..ebfdf6ebe4 100644 --- a/src/Markup/Avalonia.Markup/Data/Plugins/IPropertyAccessorPlugin.cs +++ b/src/Markup/Avalonia.Markup/Data/Plugins/IPropertyAccessorPlugin.cs @@ -14,9 +14,10 @@ namespace Avalonia.Markup.Data.Plugins /// /// Checks whether this plugin can handle accessing the properties of the specified object. /// - /// A weak reference to the object. - /// True if the plugin can handle the object; otherwise false. - bool Match(WeakReference reference); + /// The object. + /// The property name. + /// True if the plugin can handle the property on the object; otherwise false. + bool Match(object obj, string propertyName); /// /// Starts monitoring the value of a property on an object. diff --git a/src/Markup/Avalonia.Markup/Data/Plugins/InpcPropertyAccessorPlugin.cs b/src/Markup/Avalonia.Markup/Data/Plugins/InpcPropertyAccessorPlugin.cs index 138f09b373..3bdaba6fd9 100644 --- a/src/Markup/Avalonia.Markup/Data/Plugins/InpcPropertyAccessorPlugin.cs +++ b/src/Markup/Avalonia.Markup/Data/Plugins/InpcPropertyAccessorPlugin.cs @@ -19,7 +19,7 @@ namespace Avalonia.Markup.Data.Plugins public class InpcPropertyAccessorPlugin : IPropertyAccessorPlugin { /// - public bool Match(WeakReference reference) => true; + public bool Match(object obj, string propertyName) => true; /// /// Starts monitoring the value of a property on an object. @@ -36,7 +36,7 @@ namespace Avalonia.Markup.Data.Plugins Contract.Requires(propertyName != null); var instance = reference.Target; - var p = instance.GetType().GetRuntimeProperties().FirstOrDefault(_ => _.Name == propertyName); + var p = instance.GetType().GetRuntimeProperties().FirstOrDefault(x => x.Name == propertyName); if (p != null) { @@ -138,7 +138,7 @@ namespace Avalonia.Markup.Data.Plugins if (inpc != null) { - WeakSubscriptionManager.Subscribe( + WeakSubscriptionManager.Subscribe( inpc, nameof(inpc.PropertyChanged), this); diff --git a/src/Markup/Avalonia.Markup/Data/PropertyAccessorNode.cs b/src/Markup/Avalonia.Markup/Data/PropertyAccessorNode.cs index ab80d72215..f6040d3f15 100644 --- a/src/Markup/Avalonia.Markup/Data/PropertyAccessorNode.cs +++ b/src/Markup/Avalonia.Markup/Data/PropertyAccessorNode.cs @@ -37,7 +37,7 @@ namespace Avalonia.Markup.Data protected override IObservable StartListeningCore(WeakReference reference) { - var plugin = ExpressionObserver.PropertyAccessors.FirstOrDefault(x => x.Match(reference)); + var plugin = ExpressionObserver.PropertyAccessors.FirstOrDefault(x => x.Match(reference.Target, PropertyName)); var accessor = plugin?.Start(reference, PropertyName); if (_enableValidation && Next == null) diff --git a/tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_AvaloniaProperty.cs b/tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_AvaloniaProperty.cs index cd691daaf9..6d1e9d94a9 100644 --- a/tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_AvaloniaProperty.cs +++ b/tests/Avalonia.Markup.UnitTests/Data/ExpressionObserverTests_AvaloniaProperty.cs @@ -30,6 +30,16 @@ namespace Avalonia.Markup.UnitTests.Data Assert.Null(((IAvaloniaObjectDebug)data).GetPropertyChangedSubscribers()); } + [Fact] + public async Task Should_Get_Simple_ClrProperty_Value() + { + var data = new Class1(); + var target = new ExpressionObserver(data, "ClrProperty"); + var result = await target.Take(1); + + Assert.Equal("clr-property", result); + } + [Fact] public void Should_Track_Simple_Property_Value() { @@ -69,6 +79,8 @@ namespace Avalonia.Markup.UnitTests.Data { public static readonly StyledProperty FooProperty = AvaloniaProperty.Register("Foo", defaultValue: "foo"); + + public string ClrProperty { get; } = "clr-property"; } } }