Factories and Views

This commit is contained in:
Jöran Malek 2024-02-10 10:38:28 +01:00
parent 4e7dfc56a8
commit 2529b728ba
16 changed files with 188 additions and 4 deletions

View file

@ -1,6 +1,7 @@
using InkForge.Common.Controllers; using InkForge.Common.Controllers;
using InkForge.Common.Data; using InkForge.Common.Data;
using InkForge.Common.ViewModels; using InkForge.Common.ViewModels;
using InkForge.Common.ViewModels.Landing;
using InkForge.Common.Views; using InkForge.Common.Views;
using InkForge.Data; using InkForge.Data;
@ -16,6 +17,8 @@ public static class InkForgeServiceCollections
services.AddDbContextFactory<NoteDbContext, NoteDbContextFactory>(); services.AddDbContextFactory<NoteDbContext, NoteDbContextFactory>();
services.AddSingleton<LandingViewModel>();
services.AddSingleton<LandingViewModelFactory>();
services.AddSingleton<WorkspaceController>(); services.AddSingleton<WorkspaceController>();
services.AddTransient<IViewFor<LandingViewModel>, LandingView>(); services.AddTransient<IViewFor<LandingViewModel>, LandingView>();

View file

@ -0,0 +1,22 @@
namespace Microsoft.Extensions.DependencyInjection
{
public static class TypeFactory<TFactory, T>
where TFactory : struct, IObjectParameters<TFactory>
{
private static ObjectFactory<T>? s_objectFactory;
public static T Create(in TFactory factory, IServiceProvider serviceProvider)
{
s_objectFactory ??= ActivatorUtilities.CreateFactory<T>(TFactory.Types);
return s_objectFactory(serviceProvider, (object[])factory);
}
}
public interface IObjectParameters<T>
where T : struct, IObjectParameters<T>
{
abstract static Type[] Types { get; }
abstract static implicit operator object[](in T self);
}
}

View file

@ -0,0 +1,10 @@
using ReactiveUI;
namespace InkForge.Common.ReactiveUI;
public abstract class RoutableReactiveObject(IScreen screen) : ReactiveObject, IRoutableViewModel
{
public abstract string? UrlPathSegment { get; }
public IScreen HostScreen => screen;
}

View file

@ -0,0 +1,23 @@
using Microsoft.Extensions.DependencyInjection;
namespace InkForge.Common.ReactiveUI;
public interface IViewModelFactory<T, TCreator>
{
abstract static ObjectFactory<T> CreateObjectFactory();
abstract static TCreator GetCreator(ObjectFactory<T> factory, IServiceProvider serviceProvider);
}
public class ViewModelFactory<T, TFactory, TCreator>
where TFactory : IViewModelFactory<T, TCreator>
where TCreator : Delegate
{
private static ObjectFactory<T>? s_factory;
public TCreator CreateFactory(IServiceProvider serviceProvider)
{
s_factory ??= TFactory.CreateObjectFactory();
return TFactory.GetCreator(s_factory, serviceProvider);
}
}

View file

@ -14,8 +14,8 @@ public class AppViewModel : ReactiveObject
set => this.RaiseAndSetIfChanged(ref _view, value); set => this.RaiseAndSetIfChanged(ref _view, value);
} }
public AppViewModel(WorkspaceController workspace) public AppViewModel(WorkspaceController workspace, LandingViewModel landingViewModel)
{ {
View = new LandingViewModel(); View = landingViewModel;
} }
} }

View file

@ -0,0 +1,14 @@
using InkForge.Common.ReactiveUI;
using ReactiveUI;
namespace InkForge.Common.ViewModels.Landing;
public class CreateWorkspaceViewModel : LandingViewModelBase
{
public override string? UrlPathSegment => null;
public CreateWorkspaceViewModel(LandingViewModel landing) : base(landing)
{
}
}

View file

@ -0,0 +1,8 @@
using InkForge.Common.ReactiveUI;
namespace InkForge.Common.ViewModels.Landing;
public abstract class LandingViewModelBase(LandingViewModel landing) : RoutableReactiveObject(landing)
{
public LandingViewModel Landing => landing;
}

View file

@ -0,0 +1,21 @@
using Microsoft.Extensions.DependencyInjection;
namespace InkForge.Common.ViewModels.Landing;
public class LandingViewModelFactory(IServiceProvider serviceProvider)
{
public T Create<T>(LandingViewModel landing) where T : LandingViewModelBase
{
LandingViewModelsObjectParameters objectParameters = new(landing);
return TypeFactory<LandingViewModelsObjectParameters, T>.Create(objectParameters, serviceProvider);
}
readonly record struct LandingViewModelsObjectParameters(
LandingViewModel Landing
) : IObjectParameters<LandingViewModelsObjectParameters>
{
public static Type[] Types => [typeof(LandingViewModel)];
public static implicit operator object[](in LandingViewModelsObjectParameters self) => [self.Landing];
}
}

View file

@ -0,0 +1,12 @@
using InkForge.Common.ReactiveUI;
namespace InkForge.Common.ViewModels.Landing;
public class OpenRecentViewModel : LandingViewModelBase
{
public override string? UrlPathSegment => null;
public OpenRecentViewModel(LandingViewModel landing) : base(landing)
{
}
}

View file

@ -1,8 +1,28 @@
using System.Reactive.Linq;
using InkForge.Common.ViewModels.Landing;
using Microsoft.Extensions.DependencyInjection;
using ReactiveUI; using ReactiveUI;
namespace InkForge.Common.ViewModels; namespace InkForge.Common.ViewModels;
public class LandingViewModel : ReactiveObject public class LandingViewModel : ReactiveObject, IScreen
{ {
private readonly LandingViewModelFactory _factory;
public RoutingState Router { get; } = new();
public LandingViewModel(LandingViewModelFactory factory)
{
_factory = factory;
Router.CurrentViewModel.Where(x => x is null)
.InvokeCommand<IRoutableViewModel>(Router.NavigateAndReset);
}
public void Navigate<T>() where T : LandingViewModelBase
{
}
} }

View file

@ -2,6 +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:reactiveui="http://reactiveui.net"
xmlns:ifcvm="using:InkForge.Common.ViewModels" xmlns:ifcvm="using:InkForge.Common.ViewModels"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignWidth="800" d:DesignWidth="800"
@ -9,5 +10,9 @@
x:Class="InkForge.Common.Views.LandingView" x:Class="InkForge.Common.Views.LandingView"
x:DataType="ifcvm:LandingViewModel"> x:DataType="ifcvm:LandingViewModel">
<Grid RowDefinitions="Auto, *, Auto"> <Grid RowDefinitions="Auto, *, Auto">
<Label Content="InkForge"
Grid.Row="0" />
<reactiveui:RoutedViewHost Router="{CompiledBinding Router}" />
</Grid> </Grid>
</UserControl> </UserControl>

View file

@ -0,0 +1,10 @@
<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"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
x:Class="InkForge.Common.Views.LandingViews.CreateWorkspaceView">
Welcome to Avalonia!
</UserControl>

View file

@ -0,0 +1,13 @@
using Avalonia.ReactiveUI;
using InkForge.Common.ViewModels.Landing;
namespace InkForge.Common.Views.LandingViews;
public partial class CreateWorkspaceView : ReactiveUserControl<CreateWorkspaceViewModel>
{
public CreateWorkspaceView()
{
InitializeComponent();
}
}

View file

@ -0,0 +1,10 @@
<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"
mc:Ignorable="d"
d:DesignWidth="800"
d:DesignHeight="450"
x:Class="InkForge.Common.Views.LandingViews.OpenRecentView">
Welcome to Avalonia!
</UserControl>

View file

@ -0,0 +1,13 @@
using Avalonia.ReactiveUI;
using InkForge.Common.ViewModels.Landing;
namespace InkForge.Common.Views.LandingViews;
public partial class OpenRecentView : ReactiveUserControl<OpenRecentViewModel>
{
public OpenRecentView()
{
InitializeComponent();
}
}

View file

@ -2,8 +2,8 @@
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:ifcvm="using:InkForge.Common.ViewModels"
xmlns:reactiveui="http://reactiveui.net" xmlns:reactiveui="http://reactiveui.net"
xmlns:ifcvm="using:InkForge.Common.ViewModels"
mc:Ignorable="d" mc:Ignorable="d"
d:DesignWidth="800" d:DesignWidth="800"
d:DesignHeight="450" d:DesignHeight="450"