@ -1,6 +1,4 @@
using System ;
using Avalonia.Data.Converters ;
using Avalonia.LogicalTree ;
using Avalonia.Reactive ;
using Avalonia.Styling ;
@ -41,21 +39,66 @@ namespace Avalonia.Controls
control = control ? ? throw new ArgumentNullException ( nameof ( control ) ) ;
key = key ? ? throw new ArgumentNullException ( nameof ( key ) ) ;
IResourceNode ? current = control ;
return control . TryFindResource ( key , null , out value ) ;
}
/// <summary>
/// Finds the specified resource by searching up the logical tree and then global styles.
/// </summary>
/// <param name="control">The control.</param>
/// <param name="theme">Theme used to select theme dictionary.</param>
/// <param name="key">The resource key.</param>
/// <returns>The resource, or <see cref="AvaloniaProperty.UnsetValue"/> if not found.</returns>
public static object? FindResource ( this IResourceHost control , ThemeVariant ? theme , object key )
{
control = control ? ? throw new ArgumentNullException ( nameof ( control ) ) ;
key = key ? ? throw new ArgumentNullException ( nameof ( key ) ) ;
if ( control . TryFindResource ( key , theme , out var value ) )
{
return value ;
}
return AvaloniaProperty . UnsetValue ;
}
/// <summary>
/// Tries to the specified resource by searching up the logical tree and then global styles.
/// </summary>
/// <param name="control">The control.</param>
/// <param name="key">The resource key.</param>
/// <param name="theme">Theme used to select theme dictionary.</param>
/// <param name="value">On return, contains the resource if found, otherwise null.</param>
/// <returns>True if the resource was found; otherwise false.</returns>
public static bool TryFindResource ( this IResourceHost control , object key , ThemeVariant ? theme , out object? value )
{
control = control ? ? throw new ArgumentNullException ( nameof ( control ) ) ;
key = key ? ? throw new ArgumentNullException ( nameof ( key ) ) ;
IResourceHost ? current = control ;
while ( current ! = null )
{
if ( current . TryGetResource ( key , out value ) )
if ( current . TryGetResource ( key , theme , out value ) )
{
return true ;
}
current = ( current as IStyleHost ) ? . StylingParent as IResourceNode ;
current = ( current as IStyleHost ) ? . StylingParent as IResourceHost ;
}
value = null ;
return false ;
}
/// <inheritdoc cref="IResourceNode.TryGetResource" />
public static bool TryGetResource ( this IResourceHost control , object key , out object? value )
{
control = control ? ? throw new ArgumentNullException ( nameof ( control ) ) ;
key = key ? ? throw new ArgumentNullException ( nameof ( key ) ) ;
return control . TryGetResource ( key , null , out value ) ;
}
public static IObservable < object? > GetResourceObservable (
this IResourceHost control ,
@ -95,24 +138,49 @@ namespace Avalonia.Controls
protected override void Initialize ( )
{
_ target . ResourcesChanged + = ResourcesChanged ;
if ( _ target is StyledElement themeStyleable )
{
themeStyleable . PropertyChanged + = PropertyChanged ;
}
}
protected override void Deinitialize ( )
{
_ target . ResourcesChanged - = ResourcesChanged ;
if ( _ target is StyledElement themeStyleable )
{
themeStyleable . PropertyChanged - = PropertyChanged ;
}
}
protected override void Subscribed ( IObserver < object? > observer , bool first )
{
observer . OnNext ( Convert ( _ target . FindResource ( _ key ) ) ) ;
observer . OnNext ( GetValue ( ) ) ;
}
private void ResourcesChanged ( object? sender , ResourcesChangedEventArgs e )
{
PublishNext ( Convert ( _ target . FindResource ( _ key ) ) ) ;
PublishNext ( GetValue ( ) ) ;
}
private void PropertyChanged ( object? sender , AvaloniaPropertyChangedEventArgs e )
{
if ( e . Property = = StyledElement . ActualThemeVariantProperty )
{
PublishNext ( GetValue ( ) ) ;
}
}
private object? Convert ( object? value ) = > _ converter ? . Invoke ( value ) ? ? value ;
private object? GetValue ( )
{
if ( _ target is not StyledElement themeStyleable
| | ! _ target . TryFindResource ( _ key , themeStyleable . ActualThemeVariant , out var value ) )
{
value = _ target . FindResource ( _ key ) ? ? AvaloniaProperty . UnsetValue ;
}
return _ converter ? . Invoke ( value ) ? ? value ;
}
}
private class FloatingResourceObservable : LightweightObservableBase < object? >
@ -134,7 +202,7 @@ namespace Avalonia.Controls
_ target . OwnerChanged + = OwnerChanged ;
_ owner = _ target . Owner ;
if ( _ owner is object )
if ( _ owner is not null )
{
_ owner . ResourcesChanged + = ResourcesChanged ;
}
@ -148,43 +216,68 @@ namespace Avalonia.Controls
protected override void Subscribed ( IObserver < object? > observer , bool first )
{
if ( _ target . Owner is object )
if ( _ target . Owner is not null )
{
observer . OnNext ( Convert ( _ target . Owner . FindResource ( _ key ) ) ) ;
observer . OnNext ( GetValue ( ) ) ;
}
}
private void PublishNext ( )
{
if ( _ target . Owner is object )
if ( _ target . Owner is not null )
{
PublishNext ( Convert ( _ target . Owner . FindResource ( _ key ) ) ) ;
PublishNext ( GetValue ( ) ) ;
}
}
private void OwnerChanged ( object? sender , EventArgs e )
{
if ( _ owner is object )
if ( _ owner is not null )
{
_ owner . ResourcesChanged - = ResourcesChanged ;
}
if ( _ owner is StyledElement styleable )
{
styleable . PropertyChanged + = PropertyChanged ;
}
_ owner = _ target . Owner ;
if ( _ owner is object )
if ( _ owner is not null )
{
_ owner . ResourcesChanged + = ResourcesChanged ;
}
if ( _ owner is StyledElement styleable2 )
{
styleable2 . PropertyChanged + = PropertyChanged ;
}
PublishNext ( ) ;
}
private void PropertyChanged ( object? sender , AvaloniaPropertyChangedEventArgs e )
{
if ( e . Property = = StyledElement . ActualThemeVariantProperty )
{
PublishNext ( ) ;
}
}
private void ResourcesChanged ( object? sender , ResourcesChangedEventArgs e )
{
PublishNext ( ) ;
}
private object? Convert ( object? value ) = > _ converter ? . Invoke ( value ) ? ? value ;
private object? GetValue ( )
{
if ( ! ( _ target . Owner is StyledElement themeStyleable )
| | ! _ target . Owner . TryFindResource ( _ key , themeStyleable . ActualThemeVariant , out var value ) )
{
value = _ target . Owner ? . FindResource ( _ key ) ? ? AvaloniaProperty . UnsetValue ;
}
return _ converter ? . Invoke ( value ) ? ? value ;
}
}
}
}