AvaloniaUI / Avalonia.Xaml.Behaviors

Port of Windows UWP Xaml Behaviors for Avalonia Xaml.
MIT License
390 stars 46 forks source link

Stream trigger #68

Closed ShadowsInRain closed 3 years ago

ShadowsInRain commented 3 years ago

Avalonia allows to bind to observables and tasks with caret ^ syntax, e.g. {Binding WhenFocused^}.

Problem is, there is no easy way to subscribe to streams.

Ideally I'd like to write:

    <ia:WhateverTriggerBehavior Binding="{Binding WhenFocused^}">
      <iac:FocusControlAction/>
    </ia:WhateverTriggerBehavior>
wieslawsoltes commented 3 years ago

@ShadowsInRain

Can you try this:

using System;
using Avalonia.Xaml.Interactivity;

namespace Avalonia.Xaml.Interactions.Custom
{
    /// <summary>
    /// A behavior that performs actions when the bound data produces new value.
    /// </summary>
    public sealed class ValueChangedTriggerBehavior : Trigger
    {
        static ValueChangedTriggerBehavior()
        {
            BindingProperty.Changed.Subscribe(e => OnValueChanged(e.Sender, e));
        }

        /// <summary>
        /// Identifies the <seealso cref="Binding"/> avalonia property.
        /// </summary>
        public static readonly StyledProperty<object?> BindingProperty =
            AvaloniaProperty.Register<ValueChangedTriggerBehavior, object?>(nameof(Binding));

        /// <summary>
        /// Gets or sets the bound object that the <see cref="ValueChangedTriggerBehavior"/> will listen to. This is a avalonia property.
        /// </summary>
        public object? Binding
        {
            get => GetValue(BindingProperty);
            set => SetValue(BindingProperty, value);
        }

        private static void OnValueChanged(IAvaloniaObject avaloniaObject, AvaloniaPropertyChangedEventArgs args)
        {
            if (!(avaloniaObject is ValueChangedTriggerBehavior behavior) || behavior.AssociatedObject is null)
            {
                return;
            }

            var binding = behavior.Binding;
            if (binding is { })
            {
                Interaction.ExecuteActions(behavior.AssociatedObject, behavior.Actions, args);
            }
        }
    }
}
wieslawsoltes commented 3 years ago

@ShadowsInRain https://github.com/wieslawsoltes/AvaloniaBehaviors/pull/69

ShadowsInRain commented 3 years ago

@wieslawsoltes Thanks, it works. I did something similar, except that I subscribe directly to the observable. I was afraid that binding would emit distinct values only, but that does not seems to be the case.