Further work on ui-redesign and implemented dependency injection

This commit is contained in:
Enrico Ludwig 2024-09-02 20:35:36 +02:00
parent b42d313bff
commit 4ac3a44cef
13 changed files with 89 additions and 17 deletions

View File

@ -5,14 +5,19 @@ using Avalonia;
using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core.Plugins; using Avalonia.Data.Core.Plugins;
using Avalonia.Markup.Xaml; using Avalonia.Markup.Xaml;
using BetterRaid.Services;
using BetterRaid.ViewModels; using BetterRaid.ViewModels;
using BetterRaid.Views; using BetterRaid.Views;
using Microsoft.Extensions.DependencyInjection;
using TwitchLib.Api; using TwitchLib.Api;
namespace BetterRaid; namespace BetterRaid;
public partial class App : Application public partial class App : Application
{ {
private readonly ServiceCollection _services = [];
private ServiceProvider? _provider;
internal static TwitchAPI? TwitchApi = null; internal static TwitchAPI? TwitchApi = null;
internal static int AutoUpdateDelay = 10_000; internal static int AutoUpdateDelay = 10_000;
internal static bool HasUserZnSubbed = false; internal static bool HasUserZnSubbed = false;
@ -33,10 +38,14 @@ public partial class App : Application
+ $"&response_type={TwitchOAuthResponseType}" + $"&response_type={TwitchOAuthResponseType}"
+ $"&scope={string.Join("+", TwitchOAuthScopes)}"; + $"&scope={string.Join("+", TwitchOAuthScopes)}";
internal static readonly string ChannelPlaceholderImageUrl = "https://cdn.pixabay.com/photo/2018/11/13/22/01/avatar-3814081_1280.png"; public const string ChannelPlaceholderImageUrl = "https://cdn.pixabay.com/photo/2018/11/13/22/01/avatar-3814081_1280.png";
public IServiceProvider? Provider => _provider;
public override void Initialize() public override void Initialize()
{ {
InitializeServices();
var userHomeDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); var userHomeDir = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
switch (Environment.OSVersion.Platform) switch (Environment.OSVersion.Platform)
@ -63,7 +72,16 @@ public partial class App : Application
InitTwitchClient(); InitTwitchClient();
} }
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(_provider, this);
}
private void InitializeServices()
{
_services.AddSingleton<ITwitchDataService, TwitchDataService>();
_services.AddTransient<MainWindowViewModel>();
_services.AddTransient<AboutWindowViewModel>();
_provider = _services.BuildServiceProvider();
} }
public static void InitTwitchClient(bool overrideToken = false) public static void InitTwitchClient(bool overrideToken = false)
@ -132,14 +150,25 @@ public partial class App : Application
public override void OnFrameworkInitializationCompleted() public override void OnFrameworkInitializationCompleted()
{ {
BindingPlugins.DataValidators.RemoveAt(0);
var vm = _provider?.GetRequiredService<MainWindowViewModel>();
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{ {
// Line below is needed to remove Avalonia data validation. // Line below is needed to remove Avalonia data validation.
// Without this line you will get duplicate validations from both Avalonia and CT // Without this line you will get duplicate validations from both Avalonia and CT
BindingPlugins.DataValidators.RemoveAt(0);
desktop.MainWindow = new MainWindow desktop.MainWindow = new MainWindow
{ {
//DataContext = new MainWindowViewModel() DataContext = vm
};
}
else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
{
singleViewPlatform.MainView = new MainWindow
{
DataContext = vm
}; };
} }

View File

@ -27,6 +27,7 @@
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.--> <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.1.0" /> <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.1.0" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" /> <PackageReference Include="CommunityToolkit.Mvvm" Version="8.2.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="TwitchLib" Version="3.5.3" /> <PackageReference Include="TwitchLib" Version="3.5.3" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,11 +1,22 @@
using Avalonia;
using Avalonia.Controls; using Avalonia.Controls;
using Microsoft.Extensions.DependencyInjection;
namespace BetterRaid.Extensions; namespace BetterRaid.Extensions;
public static class DataContextExtensions public static class DataContextExtensions
{ {
public static T? GetDataContextAs<T>(this T obj) where T : Window public static T? GetDataContextAs<T>(this T obj) where T : StyledElement
{ {
return obj.DataContext as T; return obj.DataContext as T;
} }
public static void InjectDataContext<T>(this StyledElement e) where T : class
{
if (Application.Current is not App { Provider: not null } app)
return;
var vm = app.Provider.GetRequiredService<T>();
e.DataContext = vm;
}
} }

View File

@ -1,7 +1,6 @@
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Avalonia.Threading;
namespace BetterRaid.Models; namespace BetterRaid.Models;

View File

@ -9,7 +9,8 @@ sealed class Program
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break. // yet and stuff might break.
[STAThread] [STAThread]
public static void Main(string[] args) => BuildAvaloniaApp() public static void Main(string[] args) =>
BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args); .StartWithClassicDesktopLifetime(args);
// Avalonia configuration, don't remove; also used by visual designer. // Avalonia configuration, don't remove; also used by visual designer.

View File

@ -1,6 +1,6 @@
namespace BetterRaid.Services; namespace BetterRaid.Services;
public class TwitchDataService public interface ITwitchDataService
{ {
} }

View File

@ -0,0 +1,6 @@
namespace BetterRaid.Services;
public class TwitchDataService : ITwitchDataService
{
}

View File

@ -0,0 +1,13 @@
using System;
using BetterRaid.Services;
namespace BetterRaid.ViewModels;
public class AboutWindowViewModel : ViewModelBase
{
public AboutWindowViewModel(ITwitchDataService s)
{
Console.WriteLine(s);
Console.WriteLine("[DEBUG] AboutWindowViewModel created");
}
}

View File

@ -1,6 +1,11 @@
using System;
namespace BetterRaid.ViewModels; namespace BetterRaid.ViewModels;
public class AddChannelWindowViewModel : ViewModelBase public class AddChannelWindowViewModel : ViewModelBase
{ {
public AddChannelWindowViewModel()
{
Console.WriteLine("[DEBUG] AddChannelWindowViewModel created");
}
} }

View File

@ -1,15 +1,12 @@
using System; using System;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Threading;
using BetterRaid.Extensions; using BetterRaid.Extensions;
using BetterRaid.Misc; using BetterRaid.Misc;
using BetterRaid.Models; using BetterRaid.Models;
using BetterRaid.Services;
using BetterRaid.Views; using BetterRaid.Views;
namespace BetterRaid.ViewModels; namespace BetterRaid.ViewModels;
@ -46,6 +43,12 @@ public partial class MainWindowViewModel : ViewModelBase
public bool IsLoggedIn => App.TwitchApi != null; public bool IsLoggedIn => App.TwitchApi != null;
public MainWindowViewModel(ITwitchDataService t)
{
Console.WriteLine(t);
Console.WriteLine("[DEBUG] MainWindowViewModel created");
}
public void ExitApplication() public void ExitApplication()
{ {
//TODO polish later //TODO polish later
@ -55,6 +58,7 @@ public partial class MainWindowViewModel : ViewModelBase
public void ShowAboutWindow(Window owner) public void ShowAboutWindow(Window owner)
{ {
var about = new AboutWindow(); var about = new AboutWindow();
about.InjectDataContext<AboutWindowViewModel>();
about.ShowDialog(owner); about.ShowDialog(owner);
about.CenterToOwner(); about.CenterToOwner();
} }

View File

@ -4,4 +4,5 @@ namespace BetterRaid.ViewModels;
public class ViewModelBase : ObservableObject public class ViewModelBase : ObservableObject
{ {
} }

View File

@ -2,8 +2,10 @@
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="clr-namespace:BetterRaid.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="BetterRaid.Views.AboutWindow" x:Class="BetterRaid.Views.AboutWindow"
x:DataType="vm:AboutWindowViewModel"
Title="About" Title="About"
MaxWidth="300" MaxWidth="300"
MinWidth="300" MinWidth="300"

View File

@ -1,10 +1,10 @@
<Window xmlns="https://github.com/avaloniaui" <Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:BetterRaid.ViewModels" xmlns:vm="using:BetterRaid.ViewModels"
xmlns:br="using:BetterRaid"
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:ai="using:AsyncImageLoader" xmlns:ai="using:AsyncImageLoader"
xmlns:betterRaid="clr-namespace:BetterRaid"
mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="800" mc:Ignorable="d" d:DesignWidth="600" d:DesignHeight="800"
Width="600" Width="600"
Height="800" Height="800"
@ -118,7 +118,7 @@
<ai:AdvancedImage Grid.Column="0" <ai:AdvancedImage Grid.Column="0"
Grid.Row="0" Grid.Row="0"
Source="{Binding ThumbnailUrl, TargetNullValue={x:Static betterRaid:App.ChannelPlaceholderImageUrl}}" /> Source="{Binding ThumbnailUrl, TargetNullValue={x:Static br:App.ChannelPlaceholderImageUrl}}" />
<Border Grid.Column="0" <Border Grid.Column="0"
Grid.Row="0" Grid.Row="0"