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
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.*

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ public class ReactiveNavigationViewModel : ViewModelBase, IScreen
public ReactiveNavigationViewModel()
{
Router = new RoutingState();


Locator.CurrentMutable.Register<HomePageViewModel>(() => new HomePageViewModel(this));
Locator.CurrentMutable.Register<DetailPageViewModel>(() => new DetailPageViewModel(this));

Locator.CurrentMutable.Register(() => new HomePageView(), typeof(IViewFor<HomePageViewModel>));
Locator.CurrentMutable.Register(() => new DetailPageView(), typeof(IViewFor<DetailPageViewModel>));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,39 @@
<vm:ReactiveNavigationViewModel />
</Design.DataContext>
<Grid RowDefinitions="Auto,*" Margin="5">
<StackPanel Orientation="Horizontal" Spacing="5">
<WrapPanel Orientation="Horizontal" ItemSpacing="5" LineSpacing="5">
<Button Content="Navigate to Details">
<Interaction.Behaviors>
<EventTriggerBehavior EventName="Click">
<NavigateAction Router="{Binding Router}"
<NavigateAction Router="{Binding Router}"
ViewModel="{Binding DetailsViewModel}" />
</EventTriggerBehavior>
</Interaction.Behaviors>
</Button>
<Button Content="Navigate to Details (Generic)">
<Interaction.Behaviors>
<EventTriggerBehavior EventName="Click">
<NavigateToAction x:TypeArguments="vm:DetailPageViewModel"
Router="{Binding Router}" />
</EventTriggerBehavior>
</Interaction.Behaviors>
</Button>
<Button Content="Navigate to Home and Reset">
<Interaction.Behaviors>
<EventTriggerBehavior EventName="Click">
<NavigateAndReset Router="{Binding Router}"
<NavigateAndReset Router="{Binding Router}"
ViewModel="{Binding HomePageViewModel}" />
</EventTriggerBehavior>
</Interaction.Behaviors>
</Button>
<Button Content="Navigate to Home and Reset (Generic)">
<Interaction.Behaviors>
<EventTriggerBehavior EventName="Click">
<NavigateToAndResetAction x:TypeArguments="vm:HomePageViewModel"
Router="{Binding Router}" />
</EventTriggerBehavior>
</Interaction.Behaviors>
</Button>
<Button Content="Navigate Back">
<Interaction.Behaviors>
<EventTriggerBehavior EventName="Click">
Expand All @@ -42,7 +58,7 @@
</EventTriggerBehavior>
</Interaction.Behaviors>
</Button>
</StackPanel>
</WrapPanel>
<rxui:RoutedViewHost Router="{Binding Router}" Grid.Row="1" />
</Grid>
</UserControl>
56 changes: 56 additions & 0 deletions src/Avalonia.Xaml.Interactions.ReactiveUI/NavigateToAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System;
using Avalonia.Xaml.Interactivity;
using ReactiveUI;
using Splat;

namespace Avalonia.Xaml.Interactions.ReactiveUI;

/// <summary>
/// An action that resolves and navigates to a view model of type <typeparamref name="TViewModel"/>.
/// </summary>
/// <typeparam name="TViewModel">The view model type to navigate to.</typeparam>
public class NavigateToAction<TViewModel>
: StyledElementAction where TViewModel : class, IRoutableViewModel
{
/// <summary>
/// Identifies the <seealso cref="Router"/> avalonia property.
/// </summary>
public static readonly StyledProperty<RoutingState?> RouterProperty =
AvaloniaProperty.Register<NavigateToAction<TViewModel>, RoutingState?>(nameof(Router));

/// <summary>
/// Gets or sets the router used for navigation. This is an avalonia property.
/// </summary>
public RoutingState? Router
{
get => GetValue(RouterProperty);
set => SetValue(RouterProperty, value);
}

/// <summary>
/// Resolves an instance of <typeparamref name="TViewModel"/> from the service locator.
/// </summary>
/// <returns>The resolved view model instance or <c>null</c> if it cannot be created.</returns>
protected virtual TViewModel? ResolveViewModel()
{
return Locator.Current.GetService<TViewModel>();
}

/// <inheritdoc />
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;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Xaml.Interactivity;
using ReactiveUI;
using Splat;

namespace Avalonia.Xaml.Interactions.ReactiveUI;

/// <summary>
/// An action that resolves and navigates to <typeparamref name="TViewModel"/> and clears the navigation stack.
/// </summary>
/// <typeparam name="TViewModel">The view model type to navigate to.</typeparam>
public class NavigateToAndResetAction<TViewModel>
: StyledElementAction where TViewModel : class, IRoutableViewModel
{
/// <summary>
/// Identifies the <seealso cref="Router"/> avalonia property.
/// </summary>
public static readonly StyledProperty<RoutingState?> RouterProperty =
AvaloniaProperty.Register<NavigateToAndResetAction<TViewModel>, RoutingState?>(nameof(Router));

/// <summary>
/// Gets or sets the router used for navigation. This is an avalonia property.
/// </summary>
public RoutingState? Router
{
get => GetValue(RouterProperty);
set => SetValue(RouterProperty, value);
}

/// <summary>
/// Resolves an instance of <typeparamref name="TViewModel"/> from the service locator.
/// </summary>
/// <returns>The resolved view model instance or <c>null</c> if it cannot be created.</returns>
protected virtual TViewModel? ResolveViewModel()
{
return Locator.Current.GetService<TViewModel>();
}

/// <inheritdoc />
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;
}
}
4 changes: 2 additions & 2 deletions src/Avalonia.Xaml.Interactivity/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Loading