diff --git a/README.md b/README.md index e68c6d17f..9afb477b5 100644 --- a/README.md +++ b/README.md @@ -765,11 +765,16 @@ This section provides an overview of all available classes and their purpose in - **NavigateAction** *Navigates to a specified `IRoutableViewModel` using a router.* +- **NavigateToAction** + *Resolves and navigates to a view model type using a router.* + - **NavigateBackAction** *Navigates back within a `RoutingState` stack.* - **NavigateAndReset** *Navigates to a view model and clears the navigation stack.* +- **NavigateToAndResetAction** + *Resolves a view model type, clears the navigation stack, and navigates to it.* - **ObservableTriggerBehavior** *Subscribes to an `IObservable` and executes actions whenever the observable emits a value.* diff --git a/samples/BehaviorsTestApplication/ViewModels/ReactiveUI/ReactiveNavigationViewModel.cs b/samples/BehaviorsTestApplication/ViewModels/ReactiveUI/ReactiveNavigationViewModel.cs index e35ab6db4..b2c1b1f89 100644 --- a/samples/BehaviorsTestApplication/ViewModels/ReactiveUI/ReactiveNavigationViewModel.cs +++ b/samples/BehaviorsTestApplication/ViewModels/ReactiveUI/ReactiveNavigationViewModel.cs @@ -10,7 +10,10 @@ public class ReactiveNavigationViewModel : ViewModelBase, IScreen public ReactiveNavigationViewModel() { Router = new RoutingState(); - + + Locator.CurrentMutable.Register(() => new HomePageViewModel(this)); + Locator.CurrentMutable.Register(() => new DetailPageViewModel(this)); + Locator.CurrentMutable.Register(() => new HomePageView(), typeof(IViewFor)); Locator.CurrentMutable.Register(() => new DetailPageView(), typeof(IViewFor)); diff --git a/samples/BehaviorsTestApplication/Views/ReactiveUI/ReactiveNavigationView.axaml b/samples/BehaviorsTestApplication/Views/ReactiveUI/ReactiveNavigationView.axaml index 7144fe936..2c3727498 100644 --- a/samples/BehaviorsTestApplication/Views/ReactiveUI/ReactiveNavigationView.axaml +++ b/samples/BehaviorsTestApplication/Views/ReactiveUI/ReactiveNavigationView.axaml @@ -11,23 +11,39 @@ - + + + - + diff --git a/src/Avalonia.Xaml.Interactions.ReactiveUI/NavigateToAction.cs b/src/Avalonia.Xaml.Interactions.ReactiveUI/NavigateToAction.cs new file mode 100644 index 000000000..0a43bc20f --- /dev/null +++ b/src/Avalonia.Xaml.Interactions.ReactiveUI/NavigateToAction.cs @@ -0,0 +1,56 @@ +using System; +using Avalonia.Xaml.Interactivity; +using ReactiveUI; +using Splat; + +namespace Avalonia.Xaml.Interactions.ReactiveUI; + +/// +/// An action that resolves and navigates to a view model of type . +/// +/// The view model type to navigate to. +public class NavigateToAction + : StyledElementAction where TViewModel : class, IRoutableViewModel +{ + /// + /// Identifies the avalonia property. + /// + public static readonly StyledProperty RouterProperty = + AvaloniaProperty.Register, RoutingState?>(nameof(Router)); + + /// + /// Gets or sets the router used for navigation. This is an avalonia property. + /// + public RoutingState? Router + { + get => GetValue(RouterProperty); + set => SetValue(RouterProperty, value); + } + + /// + /// Resolves an instance of from the service locator. + /// + /// The resolved view model instance or null if it cannot be created. + protected virtual TViewModel? ResolveViewModel() + { + return Locator.Current.GetService(); + } + + /// + public override object Execute(object? sender, object? parameter) + { + if (IsEnabled != true || Router is null) + { + return false; + } + + var vm = parameter as TViewModel ?? ResolveViewModel(); + if (vm is null) + { + return false; + } + + Router.Navigate.Execute(vm).Subscribe(); + return true; + } +} diff --git a/src/Avalonia.Xaml.Interactions.ReactiveUI/NavigateToAndResetAction.cs b/src/Avalonia.Xaml.Interactions.ReactiveUI/NavigateToAndResetAction.cs new file mode 100644 index 000000000..41608a431 --- /dev/null +++ b/src/Avalonia.Xaml.Interactions.ReactiveUI/NavigateToAndResetAction.cs @@ -0,0 +1,57 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Avalonia.Xaml.Interactivity; +using ReactiveUI; +using Splat; + +namespace Avalonia.Xaml.Interactions.ReactiveUI; + +/// +/// An action that resolves and navigates to and clears the navigation stack. +/// +/// The view model type to navigate to. +public class NavigateToAndResetAction + : StyledElementAction where TViewModel : class, IRoutableViewModel +{ + /// + /// Identifies the avalonia property. + /// + public static readonly StyledProperty RouterProperty = + AvaloniaProperty.Register, RoutingState?>(nameof(Router)); + + /// + /// Gets or sets the router used for navigation. This is an avalonia property. + /// + public RoutingState? Router + { + get => GetValue(RouterProperty); + set => SetValue(RouterProperty, value); + } + + /// + /// Resolves an instance of from the service locator. + /// + /// The resolved view model instance or null if it cannot be created. + protected virtual TViewModel? ResolveViewModel() + { + return Locator.Current.GetService(); + } + + /// + public override object Execute(object? sender, object? parameter) + { + if (IsEnabled != true || Router is null) + { + return false; + } + + var vm = parameter as TViewModel ?? ResolveViewModel(); + if (vm is null) + { + return false; + } + + Router.NavigateAndReset.Execute(vm).Subscribe(); + return true; + } +} diff --git a/src/Avalonia.Xaml.Interactivity/Properties/AssemblyInfo.cs b/src/Avalonia.Xaml.Interactivity/Properties/AssemblyInfo.cs index 469607eaf..eb1ddf4dd 100644 --- a/src/Avalonia.Xaml.Interactivity/Properties/AssemblyInfo.cs +++ b/src/Avalonia.Xaml.Interactivity/Properties/AssemblyInfo.cs @@ -8,7 +8,7 @@ [assembly: InternalsVisibleTo("Avalonia.Xaml.Interactions.Draggable, PublicKey=00240000048000009400000006020000002400005253413100040000010001002940ed211918fcf63c506fad1d3f7f958b21ff8f06fd2089398296173f9ca93a69b9b380a828bf13fa80d1745beeb917ec3692f4d10e44b4c941619fc7bbd5052b26880697e6fa3f0ce322c4fa902d20b67a48b4144371218f6d39ad39145ea1fe5484052dd51a2ee62af3acd0759bcf92aaefec03978ded3cfaa84798e92de8")] [assembly: InternalsVisibleTo("Avalonia.Xaml.Interactions.Events, PublicKey=00240000048000009400000006020000002400005253413100040000010001002940ed211918fcf63c506fad1d3f7f958b21ff8f06fd2089398296173f9ca93a69b9b380a828bf13fa80d1745beeb917ec3692f4d10e44b4c941619fc7bbd5052b26880697e6fa3f0ce322c4fa902d20b67a48b4144371218f6d39ad39145ea1fe5484052dd51a2ee62af3acd0759bcf92aaefec03978ded3cfaa84798e92de8")] [assembly: InternalsVisibleTo("Avalonia.Xaml.Interactions.Responsive, PublicKey=00240000048000009400000006020000002400005253413100040000010001002940ed211918fcf63c506fad1d3f7f958b21ff8f06fd2089398296173f9ca93a69b9b380a828bf13fa80d1745beeb917ec3692f4d10e44b4c941619fc7bbd5052b26880697e6fa3f0ce322c4fa902d20b67a48b4144371218f6d39ad39145ea1fe5484052dd51a2ee62af3acd0759bcf92aaefec03978ded3cfaa84798e92de8")] - -[assembly: InternalsVisibleTo("Xaml.Interactions, PublicKey=00240000048000009400000006020000002400005253413100040000010001002940ed211918fcf63c506fad1d3f7f958b21ff8f06fd2089398296173f9ca93a69b9b380a828bf13fa80d1745beeb917ec3692f4d10e44b4c941619fc7bbd5052b26880697e6fa3f0ce322c4fa902d20b67a48b4144371218f6d39ad39145ea1fe5484052dd51a2ee62af3acd0759bcf92aaefec03978ded3cfaa84798e92de8")] +[assembly: InternalsVisibleTo("Avalonia.Xaml.Interactions.ReactiveUI, PublicKey=00240000048000009400000006020000002400005253413100040000010001002940ed211918fcf63c506fad1d3f7f958b21ff8f06fd2089398296173f9ca93a69b9b380a828bf13fa80d1745beeb917ec3692f4d10e44b4c941619fc7bbd5052b26880697e6fa3f0ce322c4fa902d20b67a48b4144371218f6d39ad39145ea1fe5484052dd51a2ee62af3acd0759bcf92aaefec03978ded3cfaa84798e92de8")] +[assembly: InternalsVisibleTo("Avalonia.Xaml.Interactions.Scripting, PublicKey=00240000048000009400000006020000002400005253413100040000010001002940ed211918fcf63c506fad1d3f7f958b21ff8f06fd2089398296173f9ca93a69b9b380a828bf13fa80d1745beeb917ec3692f4d10e44b4c941619fc7bbd5052b26880697e6fa3f0ce322c4fa902d20b67a48b4144371218f6d39ad39145ea1fe5484052dd51a2ee62af3acd0759bcf92aaefec03978ded3cfaa84798e92de8")] [assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Xaml.Interactivity")]