using System;
using Avalonia.Reactive;
using Avalonia.Data;
namespace Avalonia
{
///
/// Provides extension methods for and related classes.
///
public static class AvaloniaObjectExtensions
{
///
/// Converts an to an .
///
/// The type produced by the observable.
/// The observable
/// An .
public static IBinding ToBinding(this IObservable source)
{
return new BindingAdaptor(source.Select(x => (object?)x));
}
///
/// Gets an observable for an .
///
/// The object.
/// The property.
///
/// An observable which fires immediately with the current value of the property on the
/// object and subsequently each time the property value changes.
///
///
/// The subscription to is created using a weak reference.
///
public static IObservable GetObservable(this AvaloniaObject o, AvaloniaProperty property)
{
return new AvaloniaPropertyObservable(
o ?? throw new ArgumentNullException(nameof(o)),
property ?? throw new ArgumentNullException(nameof(property)));
}
///
/// Gets an observable for an .
///
/// The object.
/// The property type.
/// The property.
///
/// An observable which fires immediately with the current value of the property on the
/// object and subsequently each time the property value changes.
///
///
/// The subscription to is created using a weak reference.
///
public static IObservable GetObservable(this AvaloniaObject o, AvaloniaProperty property)
{
return new AvaloniaPropertyObservable(
o ?? throw new ArgumentNullException(nameof(o)),
property ?? throw new ArgumentNullException(nameof(property)));
}
///
/// Gets an observable for an .
///
/// The object.
/// The property.
///
/// An observable which fires immediately with the current value of the property on the
/// object and subsequently each time the property value changes.
///
///
/// The subscription to is created using a weak reference.
///
public static IObservable> GetBindingObservable(
this AvaloniaObject o,
AvaloniaProperty property)
{
return new AvaloniaPropertyBindingObservable(
o ?? throw new ArgumentNullException(nameof(o)),
property ?? throw new ArgumentNullException(nameof(property)));
}
///
/// Gets an observable for an .
///
/// The object.
/// The property type.
/// The property.
///
/// An observable which fires immediately with the current value of the property on the
/// object and subsequently each time the property value changes.
///
///
/// The subscription to is created using a weak reference.
///
public static IObservable> GetBindingObservable(
this AvaloniaObject o,
AvaloniaProperty property)
{
return new AvaloniaPropertyBindingObservable(
o ?? throw new ArgumentNullException(nameof(o)),
property ?? throw new ArgumentNullException(nameof(property)));
}
///
/// Gets an observable that listens for property changed events for an
/// .
///
/// The object.
/// The property.
///
/// An observable which when subscribed pushes the property changed event args
/// each time a event is raised
/// for the specified property.
///
public static IObservable GetPropertyChangedObservable(
this AvaloniaObject o,
AvaloniaProperty property)
{
return new AvaloniaPropertyChangedObservable(
o ?? throw new ArgumentNullException(nameof(o)),
property ?? throw new ArgumentNullException(nameof(property)));
}
///
/// Binds an to an observable.
///
/// The type of the property.
/// The object.
/// The property.
/// The observable.
/// The priority of the binding.
///
/// A disposable which can be used to terminate the binding.
///
public static IDisposable Bind(
this AvaloniaObject target,
AvaloniaProperty property,
IObservable> source,
BindingPriority priority = BindingPriority.LocalValue)
{
target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property));
source = source ?? throw new ArgumentNullException(nameof(source));
return property switch
{
StyledProperty styled => target.Bind(styled, source, priority),
DirectPropertyBase direct => target.Bind(direct, source),
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type."),
};
}
///
/// Binds an to an observable.
///
/// The object.
/// The property.
/// The observable.
/// The priority of the binding.
///
/// A disposable which can be used to terminate the binding.
///
public static IDisposable Bind(
this AvaloniaObject target,
AvaloniaProperty property,
IObservable source,
BindingPriority priority = BindingPriority.LocalValue)
{
return property switch
{
StyledProperty styled => target.Bind(styled, source, priority),
DirectPropertyBase direct => target.Bind(direct, source),
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type."),
};
}
///
/// Binds a property on an to an .
///
/// The object.
/// The property to bind.
/// The binding.
///
/// An optional anchor from which to locate required context. When binding to objects that
/// are not in the logical tree, certain types of binding need an anchor into the tree in
/// order to locate named controls or resources. The parameter
/// can be used to provice this context.
///
/// An which can be used to cancel the binding.
public static IDisposable Bind(
this AvaloniaObject target,
AvaloniaProperty property,
IBinding binding,
object? anchor = null)
{
target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property));
binding = binding ?? throw new ArgumentNullException(nameof(binding));
var result = binding.Initiate(
target,
property,
anchor,
property.GetMetadata(target.GetType()).EnableDataValidation ?? false);
if (result != null)
{
return BindingOperations.Apply(target, property, result, anchor);
}
else
{
return Disposable.Empty;
}
}
///
/// Gets a value.
///
/// The type of the property.
/// The object.
/// The property.
/// The value.
public static T GetValue(this AvaloniaObject target, AvaloniaProperty property)
{
target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property));
return property switch
{
StyledProperty styled => target.GetValue(styled),
DirectPropertyBase direct => target.GetValue(direct),
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type.")
};
}
///
/// Gets an base value.
///
/// The object.
/// The property.
///
/// For styled properties, gets the value of the property excluding animated values, otherwise
/// . Note that this method does not return
/// property values that come from inherited or default values.
///
/// For direct properties returns the current value of the property.
///
public static object? GetBaseValue(
this AvaloniaObject target,
AvaloniaProperty property)
{
target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property));
return property.RouteGetBaseValue(target);
}
///
/// Gets an base value.
///
/// The object.
/// The property.
///
/// For styled properties, gets the value of the property excluding animated values, otherwise
/// . Note that this method does not return property values
/// that come from inherited or default values.
///
/// For direct properties returns the current value of the property.
///
public static Optional GetBaseValue(
this AvaloniaObject target,
AvaloniaProperty property)
{
target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property));
return property switch
{
StyledProperty styled => target.GetBaseValue(styled),
DirectPropertyBase direct => target.GetValue(direct),
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type.")
};
}
///
/// Subscribes to a property changed notifications for changes that originate from a
/// .
///
/// The type of the property change sender.
/// The property changed observable.
///
/// The method to call. The parameters are the sender and the event args.
///
/// A disposable that can be used to terminate the subscription.
public static IDisposable AddClassHandler(
this IObservable observable,
Action action)
where TTarget : AvaloniaObject
{
return observable.Subscribe(new ClassHandlerObserver(action));
}
///
/// Subscribes to a property changed notifications for changes that originate from a
/// .
///
/// The type of the property change sender.
/// /// The type of the property..
/// The property changed observable.
///
/// The method to call. The parameters are the sender and the event args.
///
/// A disposable that can be used to terminate the subscription.
public static IDisposable AddClassHandler(
this IObservable> observable,
Action> action) where TTarget : AvaloniaObject
{
return observable.Subscribe(new ClassHandlerObserver(action));
}
private class BindingAdaptor : IBinding
{
private IObservable _source;
public BindingAdaptor(IObservable source)
{
this._source = source;
}
public InstancedBinding? Initiate(
AvaloniaObject target,
AvaloniaProperty? targetProperty,
object? anchor = null,
bool enableDataValidation = false)
{
return InstancedBinding.OneWay(_source);
}
}
private class ClassHandlerObserver : IObserver>
{
private readonly Action> _action;
public ClassHandlerObserver(Action> action)
{
_action = action;
}
public void OnCompleted()
{
}
public void OnError(Exception error)
{
}
public void OnNext(AvaloniaPropertyChangedEventArgs value)
{
if (value.Sender is TTarget target)
{
_action(target, value);
}
}
}
private class ClassHandlerObserver : IObserver
{
private readonly Action _action;
public ClassHandlerObserver(Action action)
{
_action = action;
}
public void OnCompleted()
{
}
public void OnError(Exception error)
{
}
public void OnNext(AvaloniaPropertyChangedEventArgs value)
{
if (value.Sender is TTarget target)
{
_action(target, value);
}
}
}
}
}