| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396 |
- using System;
- using Avalonia.Reactive;
- using Avalonia.Data;
- namespace Avalonia
- {
- /// <summary>
- /// Provides extension methods for <see cref="AvaloniaObject"/> and related classes.
- /// </summary>
- public static class AvaloniaObjectExtensions
- {
- /// <summary>
- /// Converts an <see cref="IObservable{T}"/> to an <see cref="IBinding"/>.
- /// </summary>
- /// <typeparam name="T">The type produced by the observable.</typeparam>
- /// <param name="source">The observable</param>
- /// <returns>An <see cref="IBinding"/>.</returns>
- public static IBinding ToBinding<T>(this IObservable<T> source)
- {
- return new BindingAdaptor(source.Select(x => (object?)x));
- }
- /// <summary>
- /// Gets an observable for an <see cref="AvaloniaProperty"/>.
- /// </summary>
- /// <param name="o">The object.</param>
- /// <param name="property">The property.</param>
- /// <returns>
- /// An observable which fires immediately with the current value of the property on the
- /// object and subsequently each time the property value changes.
- /// </returns>
- /// <remarks>
- /// The subscription to <paramref name="o"/> is created using a weak reference.
- /// </remarks>
- public static IObservable<object?> GetObservable(this AvaloniaObject o, AvaloniaProperty property)
- {
- return new AvaloniaPropertyObservable<object?>(
- o ?? throw new ArgumentNullException(nameof(o)),
- property ?? throw new ArgumentNullException(nameof(property)));
- }
- /// <summary>
- /// Gets an observable for an <see cref="AvaloniaProperty"/>.
- /// </summary>
- /// <param name="o">The object.</param>
- /// <typeparam name="T">The property type.</typeparam>
- /// <param name="property">The property.</param>
- /// <returns>
- /// An observable which fires immediately with the current value of the property on the
- /// object and subsequently each time the property value changes.
- /// </returns>
- /// <remarks>
- /// The subscription to <paramref name="o"/> is created using a weak reference.
- /// </remarks>
- public static IObservable<T> GetObservable<T>(this AvaloniaObject o, AvaloniaProperty<T> property)
- {
- return new AvaloniaPropertyObservable<T>(
- o ?? throw new ArgumentNullException(nameof(o)),
- property ?? throw new ArgumentNullException(nameof(property)));
- }
- /// <summary>
- /// Gets an observable for an <see cref="AvaloniaProperty"/>.
- /// </summary>
- /// <param name="o">The object.</param>
- /// <param name="property">The property.</param>
- /// <returns>
- /// An observable which fires immediately with the current value of the property on the
- /// object and subsequently each time the property value changes.
- /// </returns>
- /// <remarks>
- /// The subscription to <paramref name="o"/> is created using a weak reference.
- /// </remarks>
- public static IObservable<BindingValue<object?>> GetBindingObservable(
- this AvaloniaObject o,
- AvaloniaProperty property)
- {
- return new AvaloniaPropertyBindingObservable<object?>(
- o ?? throw new ArgumentNullException(nameof(o)),
- property ?? throw new ArgumentNullException(nameof(property)));
- }
- /// <summary>
- /// Gets an observable for an <see cref="AvaloniaProperty"/>.
- /// </summary>
- /// <param name="o">The object.</param>
- /// <typeparam name="T">The property type.</typeparam>
- /// <param name="property">The property.</param>
- /// <returns>
- /// An observable which fires immediately with the current value of the property on the
- /// object and subsequently each time the property value changes.
- /// </returns>
- /// <remarks>
- /// The subscription to <paramref name="o"/> is created using a weak reference.
- /// </remarks>
- public static IObservable<BindingValue<T>> GetBindingObservable<T>(
- this AvaloniaObject o,
- AvaloniaProperty<T> property)
- {
- return new AvaloniaPropertyBindingObservable<T>(
- o ?? throw new ArgumentNullException(nameof(o)),
- property ?? throw new ArgumentNullException(nameof(property)));
- }
- /// <summary>
- /// Gets an observable that listens for property changed events for an
- /// <see cref="AvaloniaProperty"/>.
- /// </summary>
- /// <param name="o">The object.</param>
- /// <param name="property">The property.</param>
- /// <returns>
- /// An observable which when subscribed pushes the property changed event args
- /// each time a <see cref="AvaloniaObject.PropertyChanged"/> event is raised
- /// for the specified property.
- /// </returns>
- public static IObservable<AvaloniaPropertyChangedEventArgs> GetPropertyChangedObservable(
- this AvaloniaObject o,
- AvaloniaProperty property)
- {
- return new AvaloniaPropertyChangedObservable(
- o ?? throw new ArgumentNullException(nameof(o)),
- property ?? throw new ArgumentNullException(nameof(property)));
- }
- /// <summary>
- /// Binds an <see cref="AvaloniaProperty"/> to an observable.
- /// </summary>
- /// <typeparam name="T">The type of the property.</typeparam>
- /// <param name="target">The object.</param>
- /// <param name="property">The property.</param>
- /// <param name="source">The observable.</param>
- /// <param name="priority">The priority of the binding.</param>
- /// <returns>
- /// A disposable which can be used to terminate the binding.
- /// </returns>
- public static IDisposable Bind<T>(
- this AvaloniaObject target,
- AvaloniaProperty<T> property,
- IObservable<BindingValue<T>> 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
- {
- StyledPropertyBase<T> styled => target.Bind(styled, source, priority),
- DirectPropertyBase<T> direct => target.Bind(direct, source),
- _ => throw new NotSupportedException("Unsupported AvaloniaProperty type."),
- };
- }
- /// <summary>
- /// Binds an <see cref="AvaloniaProperty"/> to an observable.
- /// </summary>
- /// <param name="target">The object.</param>
- /// <param name="property">The property.</param>
- /// <param name="source">The observable.</param>
- /// <param name="priority">The priority of the binding.</param>
- /// <returns>
- /// A disposable which can be used to terminate the binding.
- /// </returns>
- public static IDisposable Bind<T>(
- this AvaloniaObject target,
- AvaloniaProperty<T> property,
- IObservable<T> source,
- BindingPriority priority = BindingPriority.LocalValue)
- {
- return property switch
- {
- StyledPropertyBase<T> styled => target.Bind(styled, source, priority),
- DirectPropertyBase<T> direct => target.Bind(direct, source),
- _ => throw new NotSupportedException("Unsupported AvaloniaProperty type."),
- };
- }
- /// <summary>
- /// Binds a property on an <see cref="AvaloniaObject"/> to an <see cref="IBinding"/>.
- /// </summary>
- /// <param name="target">The object.</param>
- /// <param name="property">The property to bind.</param>
- /// <param name="binding">The binding.</param>
- /// <param name="anchor">
- /// 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 <paramref name="anchor"/> parameter
- /// can be used to provice this context.
- /// </param>
- /// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns>
- 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 metadata = property.GetMetadata(target.GetType()) as IDirectPropertyMetadata;
- var result = binding.Initiate(
- target,
- property,
- anchor,
- metadata?.EnableDataValidation ?? false);
- if (result != null)
- {
- return BindingOperations.Apply(target, property, result, anchor);
- }
- else
- {
- return Disposable.Empty;
- }
- }
- /// <summary>
- /// Gets a <see cref="AvaloniaProperty"/> value.
- /// </summary>
- /// <typeparam name="T">The type of the property.</typeparam>
- /// <param name="target">The object.</param>
- /// <param name="property">The property.</param>
- /// <returns>The value.</returns>
- public static T GetValue<T>(this AvaloniaObject target, AvaloniaProperty<T> property)
- {
- target = target ?? throw new ArgumentNullException(nameof(target));
- property = property ?? throw new ArgumentNullException(nameof(property));
- return property switch
- {
- StyledPropertyBase<T> styled => target.GetValue(styled),
- DirectPropertyBase<T> direct => target.GetValue(direct),
- _ => throw new NotSupportedException("Unsupported AvaloniaProperty type.")
- };
- }
- /// <summary>
- /// Gets an <see cref="AvaloniaProperty"/> base value.
- /// </summary>
- /// <param name="target">The object.</param>
- /// <param name="property">The property.</param>
- /// <remarks>
- /// For styled properties, gets the value of the property excluding animated values, otherwise
- /// <see cref="AvaloniaProperty.UnsetValue"/>. 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.
- /// </remarks>
- 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);
- }
- /// <summary>
- /// Gets an <see cref="AvaloniaProperty"/> base value.
- /// </summary>
- /// <param name="target">The object.</param>
- /// <param name="property">The property.</param>
- /// <remarks>
- /// For styled properties, gets the value of the property excluding animated values, otherwise
- /// <see cref="Optional{T}.Empty"/>. 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.
- /// </remarks>
- public static Optional<T> GetBaseValue<T>(
- this AvaloniaObject target,
- AvaloniaProperty<T> property)
- {
- target = target ?? throw new ArgumentNullException(nameof(target));
- property = property ?? throw new ArgumentNullException(nameof(property));
- return property switch
- {
- StyledPropertyBase<T> styled => target.GetBaseValue(styled),
- DirectPropertyBase<T> direct => target.GetValue(direct),
- _ => throw new NotSupportedException("Unsupported AvaloniaProperty type.")
- };
- }
- /// <summary>
- /// Subscribes to a property changed notifications for changes that originate from a
- /// <typeparamref name="TTarget"/>.
- /// </summary>
- /// <typeparam name="TTarget">The type of the property change sender.</typeparam>
- /// <param name="observable">The property changed observable.</param>
- /// <param name="action">
- /// The method to call. The parameters are the sender and the event args.
- /// </param>
- /// <returns>A disposable that can be used to terminate the subscription.</returns>
- public static IDisposable AddClassHandler<TTarget>(
- this IObservable<AvaloniaPropertyChangedEventArgs> observable,
- Action<TTarget, AvaloniaPropertyChangedEventArgs> action)
- where TTarget : AvaloniaObject
- {
- return observable.Subscribe(new ClassHandlerObserver<TTarget>(action));
- }
- /// <summary>
- /// Subscribes to a property changed notifications for changes that originate from a
- /// <typeparamref name="TTarget"/>.
- /// </summary>
- /// <typeparam name="TTarget">The type of the property change sender.</typeparam>
- /// /// <typeparam name="TValue">The type of the property..</typeparam>
- /// <param name="observable">The property changed observable.</param>
- /// <param name="action">
- /// The method to call. The parameters are the sender and the event args.
- /// </param>
- /// <returns>A disposable that can be used to terminate the subscription.</returns>
- public static IDisposable AddClassHandler<TTarget, TValue>(
- this IObservable<AvaloniaPropertyChangedEventArgs<TValue>> observable,
- Action<TTarget, AvaloniaPropertyChangedEventArgs<TValue>> action) where TTarget : AvaloniaObject
- {
- return observable.Subscribe(new ClassHandlerObserver<TTarget, TValue>(action));
- }
- private class BindingAdaptor : IBinding
- {
- private IObservable<object?> _source;
- public BindingAdaptor(IObservable<object?> source)
- {
- this._source = source;
- }
- public InstancedBinding? Initiate(
- AvaloniaObject target,
- AvaloniaProperty? targetProperty,
- object? anchor = null,
- bool enableDataValidation = false)
- {
- return InstancedBinding.OneWay(_source);
- }
- }
-
- private class ClassHandlerObserver<TTarget, TValue> : IObserver<AvaloniaPropertyChangedEventArgs<TValue>>
- {
- private readonly Action<TTarget, AvaloniaPropertyChangedEventArgs<TValue>> _action;
- public ClassHandlerObserver(Action<TTarget, AvaloniaPropertyChangedEventArgs<TValue>> action)
- {
- _action = action;
- }
- public void OnCompleted()
- {
- }
- public void OnError(Exception error)
- {
- }
- public void OnNext(AvaloniaPropertyChangedEventArgs<TValue> value)
- {
- if (value.Sender is TTarget target)
- {
- _action(target, value);
- }
- }
- }
- private class ClassHandlerObserver<TTarget> : IObserver<AvaloniaPropertyChangedEventArgs>
- {
- private readonly Action<TTarget, AvaloniaPropertyChangedEventArgs> _action;
- public ClassHandlerObserver(Action<TTarget, AvaloniaPropertyChangedEventArgs> 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);
- }
- }
- }
- }
- }
|