Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,8 @@ This section provides an overview of all available classes and their purpose in

- **NavigateAndReset**
*Navigates to a view model and clears the navigation stack.*
- **ObservableTriggerBehavior**
*Subscribes to an `IObservable` and executes actions whenever the observable emits a value.*

### TextBox
- **TextBoxSelectAllOnGotFocusBehavior**
Expand Down
3 changes: 3 additions & 0 deletions samples/BehaviorsTestApplication/Views/MainView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,9 @@
<TabItem Header="ExecuteScriptAction">
<pages:ExecuteScriptActionView />
</TabItem>
<TabItem Header="Observable Trigger">
<pages:ObservableTriggerBehaviorView />
</TabItem>
<TabItem Header="Reactive Navigation">
<pages:ReactiveNavigationView />
</TabItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@
<TextBlock Name="Text" HorizontalAlignment="Center" VerticalAlignment="Center">
<Interaction.Behaviors>
<ValueChangedTriggerBehavior Binding="{Binding Values^}">
<ChangePropertyAction TargetObject="Text"
PropertyName="Text"
Value="{Binding Values^, StringFormat={} Value: {0}}" />
<ChangePropertyAction TargetObject="Text"
PropertyName="Text"
Value="{Binding Values^, StringFormat={} Value: {0}}" />
</ValueChangedTriggerBehavior>
</Interaction.Behaviors>
</TextBlock>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<UserControl x:Class="BehaviorsTestApplication.Views.Pages.ObservableTriggerBehaviorView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:BehaviorsTestApplication.ViewModels"
x:DataType="vm:MainWindowViewModel"
mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="450">
<Design.DataContext>
<vm:MainWindowViewModel />
</Design.DataContext>
<Grid>
<TextBlock Name="Text"
FontSize="20"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<Interaction.Behaviors>
<ObservableTriggerBehavior x:Name="Observer"
x:TypeArguments="x:Int32"
Observable="{Binding Values}">
<ChangePropertyAction TargetObject="Text"
PropertyName="Text"
Value="{Binding #Observer.Value, StringFormat={} Value: {0}}" />
</ObservableTriggerBehavior>
</Interaction.Behaviors>
</TextBlock>
</Grid>
</UserControl>
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;

namespace BehaviorsTestApplication.Views.Pages;

public partial class ObservableTriggerBehaviorView : UserControl
{
public ObservableTriggerBehaviorView()
{
InitializeComponent();
}

private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using System;
using Avalonia.Reactive;
using Avalonia.Threading;
using Avalonia.Xaml.Interactivity;

namespace Avalonia.Xaml.Interactions.Custom;

/// <summary>
/// A trigger that subscribes to an <see cref="IObservable{T}"/> and executes its actions whenever a new value is produced.
/// The emitted value is exposed through the <see cref="Value"/> property and passed to the actions as a parameter.
/// </summary>
/// <typeparam name="T">The type of the observable sequence.</typeparam>
public class ObservableTriggerBehavior<T> : StyledElementTrigger
{
/// <summary>
/// Identifies the <seealso cref="Observable"/> avalonia property.
/// </summary>
public static readonly StyledProperty<IObservable<T>?> ObservableProperty =
AvaloniaProperty.Register<ObservableTriggerBehavior<T>, IObservable<T>?>(nameof(Observable));

Check warning on line 19 in src/Avalonia.Xaml.Interactions.Custom/Core/ObservableTriggerBehavior.cs

View workflow job for this annotation

GitHub Actions / Build macos-latest

Inadvisable registration: Generic types cannot be referenced from XAML. Create a non-generic type to be the owner of this AvaloniaProperty.

Check warning on line 19 in src/Avalonia.Xaml.Interactions.Custom/Core/ObservableTriggerBehavior.cs

View workflow job for this annotation

GitHub Actions / Build ubuntu-latest

Inadvisable registration: Generic types cannot be referenced from XAML. Create a non-generic type to be the owner of this AvaloniaProperty.

/// <summary>
/// Identifies the <seealso cref="Value"/> avalonia property.
/// </summary>
public static readonly StyledProperty<T?> ValueProperty =
AvaloniaProperty.Register<ObservableTriggerBehavior<T>, T?>(nameof(Value));

Check warning on line 25 in src/Avalonia.Xaml.Interactions.Custom/Core/ObservableTriggerBehavior.cs

View workflow job for this annotation

GitHub Actions / Build macos-latest

Inadvisable registration: Generic types cannot be referenced from XAML. Create a non-generic type to be the owner of this AvaloniaProperty.

Check warning on line 25 in src/Avalonia.Xaml.Interactions.Custom/Core/ObservableTriggerBehavior.cs

View workflow job for this annotation

GitHub Actions / Build ubuntu-latest

Inadvisable registration: Generic types cannot be referenced from XAML. Create a non-generic type to be the owner of this AvaloniaProperty.

Check warning on line 25 in src/Avalonia.Xaml.Interactions.Custom/Core/ObservableTriggerBehavior.cs

View workflow job for this annotation

GitHub Actions / Build windows-latest

Inadvisable registration: Generic types cannot be referenced from XAML. Create a non-generic type to be the owner of this AvaloniaProperty.

Check warning on line 25 in src/Avalonia.Xaml.Interactions.Custom/Core/ObservableTriggerBehavior.cs

View workflow job for this annotation

GitHub Actions / Build windows-latest

Inadvisable registration: Generic types cannot be referenced from XAML. Create a non-generic type to be the owner of this AvaloniaProperty.

private IDisposable? _subscription;

/// <summary>
/// Gets or sets the observable sequence that triggers the actions. This is an avalonia property.
/// </summary>
public IObservable<T>? Observable
{
get => GetValue(ObservableProperty);
set => SetValue(ObservableProperty, value);
}

/// <summary>
/// Gets the last value received from the <see cref="Observable"/>. This is an avalonia property.
/// </summary>
public T? Value
{
get => GetValue(ValueProperty);
private set => SetCurrentValue(ValueProperty, value);
}

/// <inheritdoc />
protected override void OnAttached()
{
base.OnAttached();
Subscribe();
}

/// <inheritdoc />
protected override void OnDetaching()
{
base.OnDetaching();
_subscription?.Dispose();
_subscription = null;
}

/// <inheritdoc />
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);

if (change.Property == ObservableProperty)
{
Subscribe();
}
}

private void Subscribe()
{
_subscription?.Dispose();
var observable = Observable;
if (observable is not null)
{
_subscription = observable
.Subscribe(new AnonymousObserver<T>(value =>
{
Dispatcher.UIThread.Invoke(() =>
{
Value = value;
Execute(value);
});
}));
}
}

private void Execute(object? parameter)
{
if (AssociatedObject is null || !IsEnabled)
{
return;
}

Interaction.ExecuteActions(AssociatedObject, Actions, parameter);
}
}
Loading