diff --git a/Stellar.Avalonia/AvaloniaObjectObservableForProperty.cs b/Stellar.Avalonia/AvaloniaObjectObservableForProperty.cs index 36ff6d9..0118faa 100644 --- a/Stellar.Avalonia/AvaloniaObjectObservableForProperty.cs +++ b/Stellar.Avalonia/AvaloniaObjectObservableForProperty.cs @@ -38,33 +38,37 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged) => GetNotificationForProperty(sender, expression, propertyName, beforeChanged, suppressWarnings: false); -public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged, bool suppressWarnings) -{ - if (beforeChanged) + public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged, bool suppressWarnings) { - return Observable.Never>(); - } + // GetAffinityForObject already declines beforeChanged requests, so this is + // defensive: the PropertyChanged event cannot report values before they change. + if (beforeChanged) + { + return Observable.Never>(); + } - if (sender is not AvaloniaObject avaloniaObject) - { - throw new ArgumentException($"Sender must be an AvaloniaObject, but was {sender?.GetType().FullName ?? \"null\"}.", nameof(sender)); - } + if (sender is not AvaloniaObject avaloniaObject) + { + throw new ArgumentException($"Sender must be an AvaloniaObject, but was {sender?.GetType().FullName ?? "null"}.", nameof(sender)); + } - var property = AvaloniaPropertyRegistry.Instance.FindRegistered(sender.GetType(), propertyName); + var property = AvaloniaPropertyRegistry.Instance.FindRegistered(sender.GetType(), propertyName); - if (property is null) - { - if (suppressWarnings) + if (property is null) { - return Observable.Never>(); + if (suppressWarnings) + { + return Observable.Never>(); + } + + throw new ArgumentException($"No AvaloniaProperty named '{propertyName}' is registered on {sender.GetType().FullName}.", nameof(propertyName)); } - throw new ArgumentException($"No AvaloniaProperty named '{propertyName}' is registered on {sender.GetType().FullName}.", nameof(propertyName)); - } return Observable.Create>( observer => { -void Handler(object? _, AvaloniaPropertyChangedEventArgs args) + void Handler(object? handlerSender, AvaloniaPropertyChangedEventArgs args) + { if (args.Property == property) { observer.OnNext(new ObservedChange(sender, expression, default)); diff --git a/Stellar.Uno/DependencyObjectObservableForProperty.cs b/Stellar.Uno/DependencyObjectObservableForProperty.cs index 6018037..57ebf3e 100644 --- a/Stellar.Uno/DependencyObjectObservableForProperty.cs +++ b/Stellar.Uno/DependencyObjectObservableForProperty.cs @@ -38,29 +38,32 @@ public int GetAffinityForObject(Type type, string propertyName, bool beforeChang public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged) => GetNotificationForProperty(sender, expression, propertyName, beforeChanged, suppressWarnings: false); -public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged, bool suppressWarnings) -{ - if (beforeChanged) + public IObservable> GetNotificationForProperty(object sender, Expression expression, string propertyName, bool beforeChanged, bool suppressWarnings) { - return Observable.Never>(); - } + // GetAffinityForObject already declines beforeChanged requests, so this is + // defensive: DependencyProperty callbacks fire only after a value changes. + if (beforeChanged) + { + return Observable.Never>(); + } - if (sender is not DependencyObject dependencyObject) - { - throw new ArgumentException($"Sender must be a DependencyObject, but was {sender?.GetType().FullName ?? \"null\"}.", nameof(sender)); - } + if (sender is not DependencyObject dependencyObject) + { + throw new ArgumentException($"Sender must be a DependencyObject, but was {sender?.GetType().FullName ?? "null"}.", nameof(sender)); + } - var dependencyProperty = GetDependencyProperty(sender.GetType(), propertyName); + var dependencyProperty = GetDependencyProperty(sender.GetType(), propertyName); - if (dependencyProperty is null) - { - if (suppressWarnings) + if (dependencyProperty is null) { - return Observable.Never>(); + if (suppressWarnings) + { + return Observable.Never>(); + } + + throw new ArgumentException($"No DependencyProperty named '{propertyName}Property' was found on {sender.GetType().FullName}.", nameof(propertyName)); } - throw new ArgumentException($"No DependencyProperty named '{propertyName}Property' was found on {sender.GetType().FullName}.", nameof(propertyName)); - } return Observable.Create>( observer => {