diff --git a/Toolkit.Foundation/IProvider.cs b/Toolkit.Foundation/IProvider.cs index ce8113b..82beb4d 100644 --- a/Toolkit.Foundation/IProvider.cs +++ b/Toolkit.Foundation/IProvider.cs @@ -8,4 +8,9 @@ public interface IProvider public interface IProvider { TService? Get(); +} + +public interface ISelectable +{ + bool Selected { get; set; } } \ No newline at end of file diff --git a/Toolkit.Foundation/ObservableCollectionViewModel.cs b/Toolkit.Foundation/ObservableCollectionViewModel.cs index 4ebc292..7aaa905 100644 --- a/Toolkit.Foundation/ObservableCollectionViewModel.cs +++ b/Toolkit.Foundation/ObservableCollectionViewModel.cs @@ -97,7 +97,7 @@ public partial class ObservableCollectionViewModel : public TViewModel this[int index] { - get => collection[index]; + get => Count > 0 ? collection[index] : default; set => SetItem(index, value); } diff --git a/Toolkit.UI.Avalonia/AttachedBehavior.cs b/Toolkit.UI.Avalonia/AttachedBehaviour.cs similarity index 85% rename from Toolkit.UI.Avalonia/AttachedBehavior.cs rename to Toolkit.UI.Avalonia/AttachedBehaviour.cs index d9d61ae..b343d5c 100644 --- a/Toolkit.UI.Avalonia/AttachedBehavior.cs +++ b/Toolkit.UI.Avalonia/AttachedBehaviour.cs @@ -2,11 +2,11 @@ namespace Toolkit.UI.Avalonia; -public class AttachedBehavior : Trigger +public class AttachedBehaviour : Trigger { protected override void OnAttachedToVisualTree() { Interaction.ExecuteActions(AssociatedObject, Actions, null); base.OnAttachedToVisualTree(); } -} \ No newline at end of file +} diff --git a/Toolkit.UI.Avalonia/KeyBindingTriggerBehavior.cs b/Toolkit.UI.Avalonia/KeyBindingTriggerBehaviour.cs similarity index 83% rename from Toolkit.UI.Avalonia/KeyBindingTriggerBehavior.cs rename to Toolkit.UI.Avalonia/KeyBindingTriggerBehaviour.cs index 50c0775..8946c24 100644 --- a/Toolkit.UI.Avalonia/KeyBindingTriggerBehavior.cs +++ b/Toolkit.UI.Avalonia/KeyBindingTriggerBehaviour.cs @@ -5,12 +5,12 @@ using System.Windows.Input; namespace Toolkit.UI.Avalonia; -public class KeyBindingTriggerBehavior : +public class KeyBindingTriggerBehaviour : Trigger, ICommand { public static readonly StyledProperty GestureProperty = - AvaloniaProperty.Register(nameof(Gesture)); + AvaloniaProperty.Register(nameof(Gesture)); public KeyGesture Gesture { @@ -24,7 +24,7 @@ public class KeyBindingTriggerBehavior : { if (Gesture is not null) { - KeyBinding keyBinding = new KeyBinding + KeyBinding keyBinding = new() { Gesture = Gesture, Command = this diff --git a/Toolkit.UI.Avalonia/NavigationViewItemInvokedBehaviour.cs b/Toolkit.UI.Avalonia/NavigationViewItemInvokedBehaviour.cs new file mode 100644 index 0000000..55e6304 --- /dev/null +++ b/Toolkit.UI.Avalonia/NavigationViewItemInvokedBehaviour.cs @@ -0,0 +1,86 @@ +using Avalonia; +using Avalonia.VisualTree; +using Avalonia.Xaml.Interactivity; +using FluentAvalonia.Core; +using System.Collections; +using Toolkit.UI.Controls.Avalonia; +using ISelectable = Toolkit.Foundation.ISelectable; + +namespace Toolkit.UI.Avalonia; + +public class NavigationViewItemInvokedBehaviour : Trigger +{ + public static readonly StyledProperty SelectsChildOnInvokedProperty = + AvaloniaProperty.Register(nameof(SelectsChildOnInvoked)); + + private NavigationView? navigationView; + + public event TypedEventHandler? Invoked; + + public bool SelectsChildOnInvoked + { + get => GetValue(SelectsChildOnInvokedProperty); + set => SetValue(SelectsChildOnInvokedProperty, value); + } + + protected override void OnAttached() + { + if (AssociatedObject is NavigationViewItem navigationViewItem) + { + navigationViewItem.AttachedToVisualTree += OnAttachedToVisualTree; + } + + base.OnAttached(); + } + + protected override void OnDetachedFromVisualTree() + { + if (navigationView is not null) + { + navigationView.ItemInvoked -= OnItemInvoked; + } + + base.OnDetachedFromVisualTree(); + } + + private void OnAttachedToVisualTree(object? sender, + VisualTreeAttachmentEventArgs args) + { + if (AssociatedObject is NavigationViewItem navigationViewItem) + { + navigationViewItem.AttachedToVisualTree -= OnAttachedToVisualTree; + SetupNavigationView(); + } + } + + private void OnItemInvoked(object? sender, + FluentAvalonia.UI.Controls.NavigationViewItemInvokedEventArgs args) + { + if (AssociatedObject is NavigationViewItem navigationViewItem) + { + if (args.InvokedItemContainer == AssociatedObject) + { + Interaction.ExecuteActions(AssociatedObject, Actions, null); + if (!navigationViewItem.IsChildSelected && navigationViewItem.MenuItemsSource is IList collection) + { + if (collection is { Count: > 0 } && collection[0] is ISelectable selectable) + { + selectable.Selected = true; + } + } + } + } + } + + private void SetupNavigationView() + { + if (AssociatedObject is NavigationViewItem navigationViewItem) + { + if (navigationViewItem.GetVisualAncestors().OfType().FirstOrDefault() is NavigationView navigationView) + { + this.navigationView = navigationView; + navigationView.ItemInvoked += OnItemInvoked; + } + } + } +} diff --git a/Toolkit.UI.Avalonia/SelectNavigationViewItemAction.cs b/Toolkit.UI.Avalonia/SelectNavigationViewItemAction.cs new file mode 100644 index 0000000..50c9fc1 --- /dev/null +++ b/Toolkit.UI.Avalonia/SelectNavigationViewItemAction.cs @@ -0,0 +1,48 @@ +using Avalonia; +using Avalonia.Threading; +using Avalonia.Xaml.Interactivity; +using System.Collections; +using System.Collections.ObjectModel; +using System.Collections.Specialized; +using Toolkit.Foundation; +using Toolkit.UI.Controls.Avalonia; + +namespace Toolkit.UI.Avalonia; + +public class SelectNavigationViewItemAction : + AvaloniaObject, + IAction +{ + public object? Execute(object? sender, object? parameter) + { + if (sender is NavigationViewItem navigationViewItem) + { + Dispatcher.UIThread.Post(() => + { + if (navigationViewItem.MenuItemsSource is IList collection) + { + if (collection is { Count: > 0 } && collection[0] is ISelectable selectable) + { + selectable.Selected = true; + } + } + }, DispatcherPriority.ContextIdle); + } + + if (sender is NavigationView navigationView) + { + Dispatcher.UIThread.Post(() => + { + if (navigationView.MenuItemsSource is IList collection) + { + if (collection is { Count: > 0 } && collection[0] is ISelectable selectable) + { + selectable.Selected = true; + } + } + }, DispatcherPriority.ContextIdle); + } + + return true; + } +} diff --git a/Toolkit.UI.Avalonia/Toolkit.UI.Avalonia.csproj b/Toolkit.UI.Avalonia/Toolkit.UI.Avalonia.csproj index e84927b..53a1c8c 100644 --- a/Toolkit.UI.Avalonia/Toolkit.UI.Avalonia.csproj +++ b/Toolkit.UI.Avalonia/Toolkit.UI.Avalonia.csproj @@ -10,5 +10,6 @@ + \ No newline at end of file