Skip to content
1 change: 0 additions & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
<PackageVersion Include="Avalonia.Headless.XUnit" Version="$(AvaloniaVersion)" />
</ItemGroup>
<ItemGroup>
<PackageVersion Include="System.Reactive" Version="6.0.1" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="Nuke.Common" Version="5.3.0" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using Avalonia.Animation;
using Avalonia.Styling;
using System.Reactive.Disposables;

namespace Avalonia.Xaml.Interactions.Custom;

Expand Down Expand Up @@ -43,12 +42,11 @@ public TimeSpan Duration
/// <summary>
///
/// </summary>
/// <param name="disposable"></param>
protected override void OnAttachedToVisualTree(CompositeDisposable disposable)
protected override System.IDisposable OnAttachedToVisualTreeOverride()
{
if (AssociatedObject is null)
{
return;
return DisposableAction.Empty;
}

var totalDuration = InitialDelay + Duration;
Expand All @@ -64,5 +62,7 @@ protected override void OnAttachedToVisualTree(CompositeDisposable disposable)
}
};
animation.RunAsync(AssociatedObject);

return DisposableAction.Empty;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@

<ItemGroup>
<PackageReference Include="Avalonia" />
<PackageReference Include="System.Reactive" />
</ItemGroup>

<Import Project="..\..\build\SignAssembly.props" />
Expand Down
17 changes: 9 additions & 8 deletions src/Avalonia.Xaml.Interactions.Custom/BindPointerOverBehavior.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Reactive.Disposables;
using System;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Input;
Expand Down Expand Up @@ -28,21 +28,22 @@ public bool IsPointerOver
/// <summary>
///
/// </summary>
/// <param name="disposables"></param>
protected override void OnAttached(CompositeDisposable disposables)
/// <returns></returns>
protected override IDisposable OnAttachedOverride()
{
if (AssociatedObject is null)
{
return;
return DisposableAction.Empty;
}

var control = AssociatedObject;
control.PropertyChanged += AssociatedObjectOnPropertyChanged;

disposables.Add(Disposable.Create(() => control.PropertyChanged -= AssociatedObjectOnPropertyChanged));
disposables.Add(Disposable.Create(() => IsPointerOver = false));

return;
return DisposableAction.Create(() =>
{
control.PropertyChanged -= AssociatedObjectOnPropertyChanged;
IsPointerOver = false;
});

void AssociatedObjectOnPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Reactive.Disposables;
using Avalonia.Controls;
using Avalonia.VisualTree;

Expand All @@ -13,16 +12,17 @@ public class BindTagToVisualRootDataContextBehavior : DisposingBehavior<Control>
/// <summary>
///
/// </summary>
/// <param name="disposables"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
protected override void OnAttached(CompositeDisposable disposables)
protected override IDisposable OnAttachedOverride()
{
var visualRoot = (Control?)AssociatedObject?.GetVisualRoot();
if (visualRoot is not null)
{
var disposable = BindDataContextToTag(visualRoot, AssociatedObject);
disposables.Add(disposable);
return BindDataContextToTag(visualRoot, AssociatedObject);
}

return DisposableAction.Empty;
}

private static IDisposable BindDataContextToTag(Control source, Control? target)
Expand Down
14 changes: 8 additions & 6 deletions src/Avalonia.Xaml.Interactions.Custom/BoundsObserverBehavior.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using System.Reactive;
using System.Reactive.Disposables;
using System;
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Reactive;

namespace Avalonia.Xaml.Interactions.Custom;

Expand Down Expand Up @@ -60,17 +60,19 @@ public double Height
/// <summary>
/// Attaches the behavior to the associated control and starts observing its bounds to update the Width and Height properties accordingly.
/// </summary>
/// <param name="disposables">A composite disposable used to manage the lifecycle of subscriptions and other disposables.</param>
protected override void OnAttached(CompositeDisposable disposables)
/// <returns>A disposable resource to be disposed when the behavior is detached.</returns>
protected override IDisposable OnAttachedOverride()
{
if (AssociatedObject is not null)
{
disposables.Add(this.GetObservable(BoundsProperty)
return this.GetObservable(BoundsProperty)
.Subscribe(new AnonymousObserver<Rect>(bounds =>
{
Width = bounds.Width;
Height = bounds.Height;
})));
}));
}

return DisposableAction.Empty;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.Reactive.Disposables;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
Expand All @@ -15,14 +14,15 @@ public class ButtonExecuteCommandOnKeyDownBehavior : ExecuteCommandOnKeyBehavior
/// <summary>
///
/// </summary>
/// <param name="disposables"></param>
protected override void OnAttachedToVisualTree(CompositeDisposable disposables)
/// <returns></returns>
protected override System.IDisposable OnAttachedToVisualTreeOverride()
{
if (AssociatedObject?.GetVisualRoot() is InputElement inputRoot)
{
var disposable = inputRoot.AddDisposableHandler(InputElement.KeyDownEvent, RootDefaultKeyDown);
disposables.Add(disposable);
return inputRoot.AddDisposableHandler(InputElement.KeyDownEvent, RootDefaultKeyDown);
}

return DisposableAction.Empty;
}

private void RootDefaultKeyDown(object? sender, KeyEventArgs e)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System;
using System.Reactive.Disposables;
using Avalonia.Controls;
using Avalonia.Reactive;

namespace Avalonia.Xaml.Interactions.Custom;

Expand All @@ -27,18 +27,16 @@ public bool IsFlyoutOpen
/// <summary>
///
/// </summary>
/// <param name="disposables"></param>
protected override void OnAttached(CompositeDisposable disposables)
/// <returns></returns>
protected override IDisposable OnAttachedOverride()
{
var disposable = this.GetObservable(IsFlyoutOpenProperty)
.Subscribe(isOpen =>
return this.GetObservable(IsFlyoutOpenProperty)
.Subscribe(new AnonymousObserver<bool>(isOpen =>
{
if (!isOpen)
{
AssociatedObject?.Flyout?.Hide();
}
});

disposables.Add(disposable);
}));
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System;
using System.Reactive.Disposables;
using System.Reactive.Linq;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
Expand All @@ -16,33 +14,39 @@
/// <summary>
///
/// </summary>
/// <param name="disposables"></param>
protected override void OnAttachedToVisualTree(CompositeDisposable disposables)
/// <returns></returns>
protected override System.IDisposable OnAttachedToVisualTreeOverride()
{
if (AssociatedObject is null)
var button = AssociatedObject;

if (button is null)
{
return;
return DisposableAction.Empty;
}

var flyoutPresenter = AssociatedObject.FindAncestorOfType<FlyoutPresenter>();
var flyoutPresenter = button.FindAncestorOfType<FlyoutPresenter>();
if (flyoutPresenter?.Parent is not Popup popup)
{
return;
return DisposableAction.Empty;
}

var disposable = Observable
.FromEventPattern<RoutedEventArgs>(handler => AssociatedObject.Click += handler, handler => AssociatedObject.Click -= handler)
.Do(_ =>
{
// Execute Command if any before closing. Otherwise, it won't execute because Close will destroy the associated object before Click can execute it.
if (AssociatedObject.Command != null && AssociatedObject.IsEnabled)
{
AssociatedObject.Command.Execute(AssociatedObject.CommandParameter);
}
popup.Close();
})
.Subscribe();

disposables.Add(disposable);
button.Click += AssociatedObjectOnClick;

Check warning on line 33 in src/Avalonia.Xaml.Interactions.Custom/Button/ButtonHideFlyoutOnClickBehavior.cs

View workflow job for this annotation

GitHub Actions / Build ubuntu-latest

Nullability of reference types in type of parameter 'sender' of 'void AssociatedObjectOnClick(object sender, RoutedEventArgs e)' doesn't match the target delegate 'EventHandler<RoutedEventArgs>' (possibly because of nullability attributes).

Check warning on line 33 in src/Avalonia.Xaml.Interactions.Custom/Button/ButtonHideFlyoutOnClickBehavior.cs

View workflow job for this annotation

GitHub Actions / Build windows-latest

Nullability of reference types in type of parameter 'sender' of 'void AssociatedObjectOnClick(object sender, RoutedEventArgs e)' doesn't match the target delegate 'EventHandler<RoutedEventArgs>' (possibly because of nullability attributes).

Check warning on line 33 in src/Avalonia.Xaml.Interactions.Custom/Button/ButtonHideFlyoutOnClickBehavior.cs

View workflow job for this annotation

GitHub Actions / Build macos-latest

Nullability of reference types in type of parameter 'sender' of 'void AssociatedObjectOnClick(object sender, RoutedEventArgs e)' doesn't match the target delegate 'EventHandler<RoutedEventArgs>' (possibly because of nullability attributes).

Check warning on line 33 in src/Avalonia.Xaml.Interactions.Custom/Button/ButtonHideFlyoutOnClickBehavior.cs

View workflow job for this annotation

GitHub Actions / Build macos-latest

Nullability of reference types in type of parameter 'sender' of 'void AssociatedObjectOnClick(object sender, RoutedEventArgs e)' doesn't match the target delegate 'EventHandler<RoutedEventArgs>' (possibly because of nullability attributes).

return DisposableAction.Create(() =>
{
button.Click -= AssociatedObjectOnClick;

Check warning on line 37 in src/Avalonia.Xaml.Interactions.Custom/Button/ButtonHideFlyoutOnClickBehavior.cs

View workflow job for this annotation

GitHub Actions / Build ubuntu-latest

Nullability of reference types in type of parameter 'sender' of 'void AssociatedObjectOnClick(object sender, RoutedEventArgs e)' doesn't match the target delegate 'EventHandler<RoutedEventArgs>' (possibly because of nullability attributes).

Check warning on line 37 in src/Avalonia.Xaml.Interactions.Custom/Button/ButtonHideFlyoutOnClickBehavior.cs

View workflow job for this annotation

GitHub Actions / Build windows-latest

Nullability of reference types in type of parameter 'sender' of 'void AssociatedObjectOnClick(object sender, RoutedEventArgs e)' doesn't match the target delegate 'EventHandler<RoutedEventArgs>' (possibly because of nullability attributes).

Check warning on line 37 in src/Avalonia.Xaml.Interactions.Custom/Button/ButtonHideFlyoutOnClickBehavior.cs

View workflow job for this annotation

GitHub Actions / Build macos-latest

Nullability of reference types in type of parameter 'sender' of 'void AssociatedObjectOnClick(object sender, RoutedEventArgs e)' doesn't match the target delegate 'EventHandler<RoutedEventArgs>' (possibly because of nullability attributes).

Check warning on line 37 in src/Avalonia.Xaml.Interactions.Custom/Button/ButtonHideFlyoutOnClickBehavior.cs

View workflow job for this annotation

GitHub Actions / Build macos-latest

Nullability of reference types in type of parameter 'sender' of 'void AssociatedObjectOnClick(object sender, RoutedEventArgs e)' doesn't match the target delegate 'EventHandler<RoutedEventArgs>' (possibly because of nullability attributes).
});

void AssociatedObjectOnClick(object sender, RoutedEventArgs e)
{
// Execute Command if any before closing. Otherwise, it won't execute because Close will destroy the associated object before Click can execute it.
if (button.Command != null && button.IsEnabled)
{
button.Command.Execute(button.CommandParameter);
}
popup.Close();
}
}


}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Reactive.Disposables;
using System;
using Avalonia.Xaml.Interactivity;

namespace Avalonia.Xaml.Interactions.Custom;
Expand All @@ -9,23 +9,28 @@ namespace Avalonia.Xaml.Interactions.Custom;
/// <typeparam name="T"></typeparam>
public abstract class AttachedToLogicalTreeBehavior<T> : DisposingBehavior<T> where T : StyledElement
{
private CompositeDisposable? _disposables;
private IDisposable? _disposable;

/// <inheritdoc />
protected override void OnAttached(CompositeDisposable disposables)
protected override IDisposable OnAttachedOverride()
{
_disposables = disposables;
return new DisposableAction(OnDelayedDispose);
}

/// <inheritdoc />
protected override void OnAttachedToLogicalTree()
{
OnAttachedToLogicalTree(_disposables!);
_disposable = OnAttachedToLogicalTreeOverride();
}

/// <summary>
/// Called after the <see cref="StyledElementBehavior{T}.AssociatedObject"/> is attached to the logical tree.
/// </summary>
/// <param name="disposable">The group of disposable resources that are disposed together</param>
protected abstract void OnAttachedToLogicalTree(CompositeDisposable disposable);
/// <returns>A disposable resource to be disposed when the behavior is detached.</returns>
protected abstract IDisposable OnAttachedToLogicalTreeOverride();

private void OnDelayedDispose()
{
_disposable?.Dispose();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Reactive.Disposables;
using System;
using Avalonia.Xaml.Interactivity;

namespace Avalonia.Xaml.Interactions.Custom;
Expand All @@ -9,23 +9,28 @@ namespace Avalonia.Xaml.Interactions.Custom;
/// <typeparam name="T"></typeparam>
public abstract class AttachedToVisualTreeBehavior<T> : DisposingBehavior<T> where T : Visual
{
private CompositeDisposable? _disposables;
private IDisposable? _disposable;

/// <inheritdoc />
protected override void OnAttached(CompositeDisposable disposables)
protected override IDisposable OnAttachedOverride()
{
_disposables = disposables;
return new DisposableAction(OnDelayedDispose);
}

/// <inheritdoc />
protected override void OnAttachedToVisualTree()
{
OnAttachedToVisualTree(_disposables!);
_disposable = OnAttachedToVisualTreeOverride();
}

/// <summary>
/// Called after the <see cref="StyledElementBehavior{T}.AssociatedObject"/> is attached to the visual tree.
/// </summary>
/// <param name="disposable">The group of disposable resources that are disposed together</param>
protected abstract void OnAttachedToVisualTree(CompositeDisposable disposable);
/// <returns>A disposable resource to be disposed when the behavior is detached.</returns>
protected abstract IDisposable OnAttachedToVisualTreeOverride();

private void OnDelayedDispose()
{
_disposable?.Dispose();
}
}
11 changes: 6 additions & 5 deletions src/Avalonia.Xaml.Interactions.Custom/Core/BindingBehavior.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Reactive.Disposables;
using System;
using Avalonia.Controls;
using Avalonia.Data;

Expand Down Expand Up @@ -59,13 +59,14 @@ public IBinding? Binding
/// <summary>
///
/// </summary>
/// <param name="disposable"></param>
protected override void OnAttachedToVisualTree(CompositeDisposable disposable)
/// <returns></returns>
protected override System.IDisposable OnAttachedToVisualTreeOverride()
{
if (TargetObject is not null && TargetProperty is not null && Binding is not null)
{
var dispose = TargetObject.Bind(TargetProperty, Binding);
disposable.Add(dispose);
return TargetObject.Bind(TargetProperty, Binding);
}

return DisposableAction.Empty;
}
}
Loading
Loading