Skip to content
This repository has been archived by the owner on Nov 1, 2024. It is now read-only.

Commit

Permalink
Update and add new behaviors
Browse files Browse the repository at this point in the history
  • Loading branch information
wieslawsoltes committed Apr 24, 2024
1 parent 218ec6b commit fea28a5
Show file tree
Hide file tree
Showing 34 changed files with 1,300 additions and 127 deletions.
2 changes: 1 addition & 1 deletion samples/BehaviorsTestApplication/Views/ItemView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<Panel Background="Transparent">
<i:Interaction.Behaviors>
<iac:ShowOnDoubleTappedBehavior TargetControl="TextBoxEdit" />
<iac:ShowOnKeyDownTappedBehavior TargetControl="TextBoxEdit" />
<iac:ShowOnKeyDownBehavior TargetControl="TextBoxEdit" Key="F2" />
</i:Interaction.Behaviors>
<TextBox x:Name="TextBoxEdit"
IsVisible="False"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ namespace Avalonia.Xaml.Interactions.Custom;
/// </summary>
public class ButtonExecuteCommandOnKeyDownBehavior : AttachedToVisualTreeBehavior<Button>
{
/// <summary>
///
/// </summary>
public static readonly StyledProperty<bool> IsEnabledProperty =
AvaloniaProperty.Register<ButtonExecuteCommandOnKeyDownBehavior, bool>(nameof(IsEnabled));

/// <summary>
///
/// </summary>
Expand All @@ -20,8 +26,17 @@ public class ButtonExecuteCommandOnKeyDownBehavior : AttachedToVisualTreeBehavio
/// <summary>
///
/// </summary>
public static readonly StyledProperty<bool> IsEnabledProperty =
AvaloniaProperty.Register<ButtonExecuteCommandOnKeyDownBehavior, bool>(nameof(IsEnabled));
public static readonly StyledProperty<KeyGesture?> GestureProperty =
AvaloniaProperty.Register<ButtonExecuteCommandOnKeyDownBehavior, KeyGesture?>(nameof(Gesture));

/// <summary>
///
/// </summary>
public bool IsEnabled
{
get => GetValue(IsEnabledProperty);
set => SetValue(IsEnabledProperty, value);
}

/// <summary>
///
Expand All @@ -35,10 +50,10 @@ public Key? Key
/// <summary>
///
/// </summary>
public bool IsEnabled
public KeyGesture? Gesture
{
get => GetValue(IsEnabledProperty);
set => SetValue(IsEnabledProperty, value);
get => GetValue(GestureProperty);
set => SetValue(GestureProperty, value);
}

/// <summary>
Expand All @@ -47,13 +62,7 @@ public bool IsEnabled
/// <param name="disposables"></param>
protected override void OnAttachedToVisualTree(CompositeDisposable disposables)
{
var button = AssociatedObject;
if (button is null)
{
return;
}

if (button.GetVisualRoot() is InputElement inputRoot)
if (AssociatedObject?.GetVisualRoot() is InputElement inputRoot)
{
var disposable = inputRoot.AddDisposableHandler(InputElement.KeyDownEvent, RootDefaultKeyDown);
disposables.Add(disposable);
Expand All @@ -62,19 +71,38 @@ protected override void OnAttachedToVisualTree(CompositeDisposable disposables)

private void RootDefaultKeyDown(object? sender, KeyEventArgs e)
{
var button = AssociatedObject;
if (button is null)
var haveKey = Key is not null && e.Key == Key;
var haveGesture = Gesture is not null && Gesture.Matches(e);

if (!haveKey && !haveGesture)
{
return;
}

if (Key is { } && e.Key == Key && button.IsVisible && button.IsEnabled && IsEnabled)
if (AssociatedObject is { } button)
{
ExecuteCommand(button);
}
}

private bool ExecuteCommand(Button button)
{
if (!IsEnabled)
{
return false;
}

if (button is not { IsVisible: true, IsEnabled: true })
{
if (!e.Handled && button.Command?.CanExecute(button.CommandParameter) == true)
{
button.Command.Execute(button.CommandParameter);
e.Handled = true;
}
return false;
}

if (button.Command?.CanExecute(button.CommandParameter) != true)
{
return false;
}

button.Command.Execute(button.CommandParameter);
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ namespace Avalonia.Xaml.Interactions.Custom;
/// </summary>
public abstract class ExecuteCommandBehaviorBase : AttachedToVisualTreeBehavior<Control>
{
/// <summary>
///
/// </summary>
public static readonly StyledProperty<bool> IsEnabledProperty =
AvaloniaProperty.Register<ExecuteCommandBehaviorBase, bool>(nameof(IsEnabled), true);

/// <summary>
///
/// </summary>
Expand All @@ -34,6 +40,15 @@ public abstract class ExecuteCommandBehaviorBase : AttachedToVisualTreeBehavior<
public static readonly StyledProperty<Control?> FocusControlProperty =
AvaloniaProperty.Register<ExecuteCommandBehaviorBase, Control?>(nameof(CommandParameter));

/// <summary>
///
/// </summary>
public bool IsEnabled
{
get => GetValue(IsEnabledProperty);
set => SetValue(IsEnabledProperty, value);
}

/// <summary>
///
/// </summary>
Expand Down Expand Up @@ -77,6 +92,11 @@ public Control? FocusControl
/// <returns></returns>
protected bool ExecuteCommand()
{
if (!IsEnabled)
{
return false;
}

if (AssociatedObject is not { IsVisible: true, IsEnabled: true })
{
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,16 @@ protected override void OnAttachedToVisualTree(CompositeDisposable disposable)
{
var mainWindow = lifetime.MainWindow;

var dispose = Observable
.FromEventPattern(mainWindow, nameof(mainWindow.Activated))
.Subscribe(new AnonymousObserver<EventPattern<object>>(e =>
{
ExecuteCommand();
}));
disposable.Add(dispose);
if (mainWindow is not null)
{
var dispose = Observable

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

View workflow job for this annotation

GitHub Actions / Build macos-latest

Using member 'System.Reactive.Linq.Observable.FromEventPattern(Object, String)' which has 'RequiresUnreferencedCodeAttribute' can break functionality when trimming application code. This member uses reflection to discover event members and associated delegate types.
.FromEventPattern(mainWindow, nameof(mainWindow.Activated))
.Subscribe(new AnonymousObserver<EventPattern<object>>(e =>
{
ExecuteCommand();
}));
disposable.Add(dispose);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,3 @@ private void AssociatedObject_DoubleTapped(object? sender, RoutedEventArgs e)
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Reactive.Disposables;
using Avalonia.Input;
using Avalonia.Interactivity;

namespace Avalonia.Xaml.Interactions.Custom;

/// <summary>
///
/// </summary>
public class ExecuteCommandOnGotFocusBehavior : ExecuteCommandBehaviorBase
{
/// <summary>
///
/// </summary>
/// <param name="disposable"></param>
protected override void OnAttachedToVisualTree(CompositeDisposable disposable)
{
var dispose = AssociatedObject?
.AddDisposableHandler(
InputElement.GotFocusEvent,
AssociatedObject_GotFocus,
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);

if (dispose is not null)
{
disposable.Add(dispose);
}
}

private void AssociatedObject_GotFocus(object? sender, RoutedEventArgs e)
{
if (e.Handled)
{
return;
}

if (ExecuteCommand())
{
e.Handled = true;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Reactive.Disposables;
using Avalonia.Input;
using Avalonia.Interactivity;

namespace Avalonia.Xaml.Interactions.Custom;

/// <summary>
///
/// </summary>
public class ExecuteCommandOnHoldingBehavior : ExecuteCommandBehaviorBase
{
/// <summary>
///
/// </summary>
/// <param name="disposable"></param>
protected override void OnAttachedToVisualTree(CompositeDisposable disposable)
{
var dispose = AssociatedObject?
.AddDisposableHandler(
Gestures.HoldingEvent,
AssociatedObject_Holding,
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);

if (dispose is not null)
{
disposable.Add(dispose);
}
}

private void AssociatedObject_Holding(object? sender, RoutedEventArgs e)
{
if (e.Handled)
{
return;
}

if (ExecuteCommand())
{
e.Handled = true;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,78 @@ private void OnKeyDown(object? sender, KeyEventArgs e)
}
}
}

/// <summary>
///
/// </summary>
public class ExecuteCommandOnKeyUpBehavior : ExecuteCommandBehaviorBase
{
/// <summary>
///
/// </summary>
public static readonly StyledProperty<Key?> KeyProperty =
AvaloniaProperty.Register<ExecuteCommandOnKeyDownBehavior, Key?>(nameof(Key));

/// <summary>
///
/// </summary>
public static readonly StyledProperty<KeyGesture?> GestureProperty =
AvaloniaProperty.Register<ExecuteCommandOnKeyDownBehavior, KeyGesture?>(nameof(Gesture));

/// <summary>
///
/// </summary>
public Key? Key
{
get => GetValue(KeyProperty);
set => SetValue(KeyProperty, value);
}

/// <summary>
///
/// </summary>
public KeyGesture? Gesture
{
get => GetValue(GestureProperty);
set => SetValue(GestureProperty, value);
}

/// <summary>
///
/// </summary>
/// <param name="disposable"></param>
protected override void OnAttachedToVisualTree(CompositeDisposable disposable)
{
var dispose = AssociatedObject?
.AddDisposableHandler(
InputElement.KeyUpEvent,
OnKeyUp,
RoutingStrategies.Bubble);

if (dispose is not null)
{
disposable.Add(dispose);
}
}

private void OnKeyUp(object? sender, KeyEventArgs e)
{
var haveKey = Key is not null && e.Key == Key;
var haveGesture = Gesture is not null && Gesture.Matches(e);

if (!haveKey && !haveGesture)
{
return;
}

if (e.Handled)
{
return;
}

if (ExecuteCommand())
{
e.Handled = true;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Reactive.Disposables;
using Avalonia.Input;
using Avalonia.Interactivity;

namespace Avalonia.Xaml.Interactions.Custom;

/// <summary>
///
/// </summary>
public class ExecuteCommandOnPinchBehavior : ExecuteCommandBehaviorBase
{
/// <summary>
///
/// </summary>
/// <param name="disposable"></param>
protected override void OnAttachedToVisualTree(CompositeDisposable disposable)
{
var dispose = AssociatedObject?
.AddDisposableHandler(
Gestures.PinchEvent,
AssociatedObject_Pinch,
RoutingStrategies.Tunnel | RoutingStrategies.Bubble);

if (dispose is not null)
{
disposable.Add(dispose);
}
}

private void AssociatedObject_Pinch(object? sender, RoutedEventArgs e)
{
if (e.Handled)
{
return;
}

if (ExecuteCommand())
{
e.Handled = true;
}
}
}
Loading

0 comments on commit fea28a5

Please sign in to comment.