using System; using Avalonia.Data.Converters; using Avalonia.LogicalTree; using Avalonia.Reactive; using Avalonia.Styling; #nullable enable namespace Avalonia.Controls { public static class ResourceNodeExtensions { /// /// Finds the specified resource by searching up the logical tree and then global styles. /// /// The control. /// The resource key. /// The resource, or if not found. public static object? FindResource(this IResourceHost control, object key) { control = control ?? throw new ArgumentNullException(nameof(control)); key = key ?? throw new ArgumentNullException(nameof(key)); if (control.TryFindResource(key, out var value)) { return value; } return AvaloniaProperty.UnsetValue; } /// /// Tries to the specified resource by searching up the logical tree and then global styles. /// /// The control. /// The resource key. /// On return, contains the resource if found, otherwise null. /// True if the resource was found; otherwise false. public static bool TryFindResource(this IResourceHost control, object key, out object? value) { control = control ?? throw new ArgumentNullException(nameof(control)); key = key ?? throw new ArgumentNullException(nameof(key)); IResourceNode? current = control; while (current != null) { if (current.TryGetResource(key, out value)) { return true; } current = (current as IStyleHost)?.StylingParent as IResourceNode; } value = null; return false; } public static IObservable GetResourceObservable( this IResourceHost control, object key, Func? converter = null) { control = control ?? throw new ArgumentNullException(nameof(control)); key = key ?? throw new ArgumentNullException(nameof(key)); return new ResourceObservable(control, key, converter); } public static IObservable GetResourceObservable( this IResourceProvider resourceProvider, object key, Func? converter = null) { resourceProvider = resourceProvider ?? throw new ArgumentNullException(nameof(resourceProvider)); key = key ?? throw new ArgumentNullException(nameof(key)); return new FloatingResourceObservable(resourceProvider, key, converter); } private class ResourceObservable : LightweightObservableBase { private readonly IResourceHost _target; private readonly object _key; private readonly Func? _converter; public ResourceObservable(IResourceHost target, object key, Func? converter) { _target = target; _key = key; _converter = converter; } protected override void Initialize() { _target.ResourcesChanged += ResourcesChanged; } protected override void Deinitialize() { _target.ResourcesChanged -= ResourcesChanged; } protected override void Subscribed(IObserver observer, bool first) { observer.OnNext(Convert(_target.FindResource(_key))); } private void ResourcesChanged(object? sender, ResourcesChangedEventArgs e) { PublishNext(Convert(_target.FindResource(_key))); } private object? Convert(object? value) => _converter?.Invoke(value) ?? value; } private class FloatingResourceObservable : LightweightObservableBase { private readonly IResourceProvider _target; private readonly object _key; private readonly Func? _converter; private IResourceHost? _owner; public FloatingResourceObservable(IResourceProvider target, object key, Func? converter) { _target = target; _key = key; _converter = converter; } protected override void Initialize() { _target.OwnerChanged += OwnerChanged; _owner = _target.Owner; if (_owner is object) { _owner.ResourcesChanged += ResourcesChanged; } } protected override void Deinitialize() { _target.OwnerChanged -= OwnerChanged; _owner = null; } protected override void Subscribed(IObserver observer, bool first) { if (_target.Owner is object) { observer.OnNext(Convert(_target.Owner.FindResource(_key))); } } private void PublishNext() { if (_target.Owner is object) { PublishNext(Convert(_target.Owner.FindResource(_key))); } } private void OwnerChanged(object? sender, EventArgs e) { if (_owner is object) { _owner.ResourcesChanged -= ResourcesChanged; } _owner = _target.Owner; if (_owner is object) { _owner.ResourcesChanged += ResourcesChanged; } PublishNext(); } private void ResourcesChanged(object? sender, ResourcesChangedEventArgs e) { PublishNext(); } private object? Convert(object? value) => _converter?.Invoke(value) ?? value; } } }