Non working start to a NavigationStack.

This commit is contained in:
2026-02-01 20:07:06 +01:00
parent e4dd1b6150
commit ed5f941ba8
13 changed files with 145 additions and 20 deletions

View File

@@ -17,6 +17,10 @@
<PackageVersion Include="Avalonia.Android" Version="11.3.10" /> <PackageVersion Include="Avalonia.Android" Version="11.3.10" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0" /> <PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.2" /> <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.2" />
<PackageVersion Include="ReactiveUI.SourceGenerators" Version="2.6.1">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageVersion>
<PackageVersion Include="Refit" Version="9.0.2" /> <PackageVersion Include="Refit" Version="9.0.2" />
<PackageVersion Include="Xamarin.AndroidX.Core.SplashScreen" Version="1.0.1.15" /> <PackageVersion Include="Xamarin.AndroidX.Core.SplashScreen" Version="1.0.1.15" />
</ItemGroup> </ItemGroup>

View File

@@ -3,13 +3,14 @@ using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core.Plugins; using Avalonia.Data.Core.Plugins;
using System.Linq; using System.Linq;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
using Gamenight.Ui.ViewModels; using Gamenight.Ui.ViewModels;
using Gamenight.Ui.Views; using Gamenight.Ui.Views;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Gamenight.Ui.Services; using Gamenight.Ui.Services;
using System; using System;
using Refit; using Refit;
using Gamenight.Ui.Models;
using ReactiveUI;
namespace Gamenight.Ui; namespace Gamenight.Ui;
@@ -26,13 +27,21 @@ public partial class App : Application
{ {
// Register all the services needed for the application to run // Register all the services needed for the application to run
var collection = new ServiceCollection(); var collection = new ServiceCollection();
var state = new GamenightState();
collection.AddSingleton(state);
var gamenightApi = RestService.For<IGamenightApi>("http://localhost:8080", var gamenightApi = RestService.For<IGamenightApi>("http://localhost:8080",
new RefitSettings {} new RefitSettings
{
AuthorizationHeaderValueGetter = state.GetBearerToken
}
); );
collection.AddSingleton(gamenightApi); collection.AddSingleton(gamenightApi);
collection.AddSingleton<MainViewModel>(); collection.AddSingleton<MainViewModel>();
collection.AddSingleton<IScreen>(sp => sp.GetRequiredService<MainViewModel>());
collection.AddSingleton<HeaderViewModel>(); collection.AddSingleton<HeaderViewModel>();
collection.AddSingleton<SideBarViewModel>(); collection.AddSingleton<SideBarViewModel>();
collection.AddSingleton<GamenightsViewModel>();
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime) if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime)
{ {
collection.AddSingleton<MainWindow>(); collection.AddSingleton<MainWindow>();
@@ -45,7 +54,6 @@ public partial class App : Application
ServiceProvider = collection.BuildServiceProvider(); ServiceProvider = collection.BuildServiceProvider();
var mainViewModel = ServiceProvider.GetRequiredService<MainViewModel>(); var mainViewModel = ServiceProvider.GetRequiredService<MainViewModel>();
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{ {
// Avoid duplicate validations from both Avalonia and the CommunityToolkit. // Avoid duplicate validations from both Avalonia and the CommunityToolkit.

View File

@@ -22,6 +22,10 @@
</PackageReference> </PackageReference>
<PackageReference Include="CommunityToolkit.Mvvm" /> <PackageReference Include="CommunityToolkit.Mvvm" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" />
<PackageReference Include="ReactiveUI.SourceGenerators">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Refit" /> <PackageReference Include="Refit" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,13 @@
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
namespace Gamenight.Ui.Models;
public class GamenightState
{
public string JwtToken { get; set; } = "";
public Task<string> GetBearerToken(HttpRequestMessage req, CancellationToken ct) => Task.FromResult($"Bearer: {JwtToken}");
}

View File

@@ -3,6 +3,8 @@ using System.Diagnostics.CodeAnalysis;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Templates; using Avalonia.Controls.Templates;
using Gamenight.Ui.ViewModels; using Gamenight.Ui.ViewModels;
using Gamenight.Ui.Views;
using ReactiveUI;
namespace Gamenight.Ui; namespace Gamenight.Ui;
@@ -12,7 +14,7 @@ namespace Gamenight.Ui;
[RequiresUnreferencedCode( [RequiresUnreferencedCode(
"Default implementation of ViewLocator involves reflection which may be trimmed away.", "Default implementation of ViewLocator involves reflection which may be trimmed away.",
Url = "https://docs.avaloniaui.net/docs/concepts/view-locator")] Url = "https://docs.avaloniaui.net/docs/concepts/view-locator")]
public class ViewLocator : IDataTemplate public class ViewLocator : IDataTemplate, ReactiveUI.IViewLocator
{ {
public Control? Build(object? param) public Control? Build(object? param)
{ {
@@ -34,4 +36,10 @@ public class ViewLocator : IDataTemplate
{ {
return data is ViewModelBase; return data is ViewModelBase;
} }
public IViewFor ResolveView<T>(T? viewModel, string? contract = null) => viewModel switch
{
GamenightsViewModel context => new GamenightsView { DataContext = context },
_ => throw new ArgumentOutOfRangeException(nameof(viewModel))
};
} }

View File

@@ -0,0 +1,24 @@
using System;
using Gamenight.Ui.Services;
using ReactiveUI;
namespace Gamenight.Ui.ViewModels;
public class GamenightsViewModel : ReactiveObject, IRoutableViewModel
{
private IGamenightApi GamenightApi { get; }
public IScreen HostScreen { get; }
public string? UrlPathSegment { get; } = Guid.NewGuid().ToString().Substring(0, 5);
public GamenightsViewModel(IGamenightApi gamenightApi, IScreen hostScreen)
{
GamenightApi = gamenightApi;
HostScreen = hostScreen;
}
public GamenightsViewModel()
{
throw new NotImplementedException();
}
}

View File

@@ -1,33 +1,34 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input;
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using Gamenight.Ui.Services; using Gamenight.Ui.Services;
using ReactiveUI; using ReactiveUI;
using ReactiveUI.SourceGenerators;
namespace Gamenight.Ui.ViewModels; namespace Gamenight.Ui.ViewModels;
public partial class MainViewModel : ViewModelBase public partial class MainViewModel : ReactiveObject, IScreen
{ {
[ObservableProperty] [ObservableAsProperty]
private string _greeting = "Welcome to Avalonia!"; private string _greeting = "Welcome to Avalonia!";
private IGamenightApi GamenightApi { get; } private IGamenightApi GamenightApi { get; }
public HeaderViewModel HeaderViewModel { get; } public HeaderViewModel HeaderViewModel { get; }
public SideBarViewModel SideBarViewModel { get; } public SideBarViewModel SideBarViewModel { get; }
[ObservableProperty] public RoutingState Router { get; } = new RoutingState();
private ICommand _loginCommand;
[ObservableAsProperty]
private IReactiveCommand _loginCommand;
public MainViewModel(IGamenightApi gamenightApi, HeaderViewModel headerViewModel, SideBarViewModel sideBarViewModel) public MainViewModel(IGamenightApi gamenightApi, HeaderViewModel headerViewModel, SideBarViewModel sideBarViewModel)
{ {
GamenightApi = gamenightApi; GamenightApi = gamenightApi;
HeaderViewModel = headerViewModel; HeaderViewModel = headerViewModel;
SideBarViewModel = sideBarViewModel; SideBarViewModel = sideBarViewModel;
SideBarViewModel.Screen = this;
_loginCommand = ReactiveCommand.Create(Test); _loginCommand = ReactiveCommand.Create(Test);
} }
public MainViewModel() => throw new System.NotImplementedException();
public async Task Test() { public async Task Test() {
var token = await GamenightApi.Token(new Login() { var token = await GamenightApi.Token(new Login() {
Username = "admin", Username = "admin",

View File

@@ -1,7 +1,29 @@
using System.Reactive;
using ReactiveUI;
namespace Gamenight.Ui.ViewModels; namespace Gamenight.Ui.ViewModels;
public class SideBarViewModel : ViewModelBase public partial class SideBarViewModel : ReactiveObject
{ {
public IReactiveCommand<IRoutableViewModel, IRoutableViewModel> PushViewModel
{
get;
set => this.RaiseAndSetIfChanged(ref field, value);
}
public GamenightsViewModel GamenightsViewModel
{
get;
set => this.RaiseAndSetIfChanged(ref field, value);
}
public IScreen Screen { get; set; }
public SideBarViewModel(GamenightsViewModel gamenightsViewModel)
{
PushViewModel = ReactiveCommand.CreateFromObservable((IRoutableViewModel x) => Screen.Router.Navigate.Execute(x));
GamenightsViewModel = gamenightsViewModel;
}
} }

View File

@@ -0,0 +1,14 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:Gamenight.Ui.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Gamenight.Ui.Views.GamenightsView"
x:DataType="vm:GamenightsViewModel">
<Design.DataContext>
<!-- This only sets the DataContext for the previewer in an IDE,
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<vm:GamenightsViewModel />
</Design.DataContext>
</UserControl>

View File

@@ -0,0 +1,18 @@
using Avalonia.Markup.Xaml;
using Avalonia.ReactiveUI;
using Gamenight.Ui.ViewModels;
namespace Gamenight.Ui.Views;
public partial class GamenightsView : ReactiveUserControl<GamenightsViewModel>
{
public GamenightsView()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}

View File

@@ -2,8 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:Gamenight.Ui.ViewModels" xmlns:vm="using:Gamenight.Ui.ViewModels"
xmlns:views="clr-namespace:Gamenight.Ui.Views"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Gamenight.Ui.Views.HeaderView" x:Class="Gamenight.Ui.Views.HeaderView"
x:DataType="vm:HeaderViewModel"> x:DataType="vm:HeaderViewModel">

View File

@@ -3,7 +3,9 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:Gamenight.Ui.ViewModels" xmlns:vm="using:Gamenight.Ui.ViewModels"
xmlns:views="clr-namespace:Gamenight.Ui.Views" xmlns:views="clr-namespace:Gamenight.Ui.Views"
xmlns:reactiveUi="http://reactiveui.net"
xmlns:ui="clr-namespace:Gamenight.Ui"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Gamenight.Ui.Views.MainView" x:Class="Gamenight.Ui.Views.MainView"
x:DataType="vm:MainViewModel"> x:DataType="vm:MainViewModel">
@@ -24,10 +26,17 @@
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions> </Grid.ColumnDefinitions>
<views:SideBarView Grid.Column="0" DataContext="{Binding SideBarViewModel}"/> <views:SideBarView Grid.Column="0" DataContext="{Binding SideBarViewModel}"/>
<StackPanel Grid.Column="1"> <reactiveUi:RoutedViewHost Grid.Column="1" Router="{Binding Router}">
<TextBlock Text="{Binding Greeting}" HorizontalAlignment="Center" VerticalAlignment="Center" /> <reactiveUi:RoutedViewHost.DefaultContent>
<Button Content="Login" Command="{Binding LoginCommand}"/> <TextBlock Text="Default content"
</StackPanel> HorizontalAlignment="Center"
VerticalAlignment="Center" />
</reactiveUi:RoutedViewHost.DefaultContent>
<reactiveUi:RoutedViewHost.ViewLocator>
<!-- See AppViewLocator.cs section below -->
<ui:ViewLocator />
</reactiveUi:RoutedViewHost.ViewLocator>
</reactiveUi:RoutedViewHost>
</Grid> </Grid>
</Grid> </Grid>
</UserControl> </UserControl>

View File

@@ -12,4 +12,5 @@
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) --> to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
<vm:SideBarViewModel /> <vm:SideBarViewModel />
</Design.DataContext> </Design.DataContext>
<Button Content="Gamenights" Command="{Binding PushViewModel}" CommandParameter="{Binding GamenightsViewModel}"/>
</UserControl> </UserControl>