diff --git a/PcgsApi/PcgsApi.csproj b/PcgsApi/PcgsApi.csproj new file mode 100644 index 0000000..89c9676 --- /dev/null +++ b/PcgsApi/PcgsApi.csproj @@ -0,0 +1,17 @@ + + + + net10.0 + enable + enable + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + diff --git a/PcgsInv/App.axaml b/PcgsInv/App.axaml new file mode 100644 index 0000000..fe052f3 --- /dev/null +++ b/PcgsInv/App.axaml @@ -0,0 +1,15 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/PcgsInv/App.axaml.cs b/PcgsInv/App.axaml.cs new file mode 100644 index 0000000..f7e0557 --- /dev/null +++ b/PcgsInv/App.axaml.cs @@ -0,0 +1,22 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; +using PcgsInv.ViewModels; +using PcgsInv.Views; + +namespace PcgsInv; + +public class App : Application { + public override void Initialize() { + AvaloniaXamlLoader.Load(this); + } + + public override void OnFrameworkInitializationCompleted() { + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + desktop.MainWindow = new MainWindow { + DataContext = new MainWindowViewModel() + }; + + base.OnFrameworkInitializationCompleted(); + } +} \ No newline at end of file diff --git a/PcgsInv/Assets/avalonia-logo.ico b/PcgsInv/Assets/avalonia-logo.ico new file mode 100644 index 0000000..f7da8bb Binary files /dev/null and b/PcgsInv/Assets/avalonia-logo.ico differ diff --git a/PcgsInv/PcgsInv.csproj b/PcgsInv/PcgsInv.csproj new file mode 100644 index 0000000..260a592 --- /dev/null +++ b/PcgsInv/PcgsInv.csproj @@ -0,0 +1,39 @@ + + + WinExe + net10.0 + enable + app.manifest + true + + + + + + + + + + + + + + None + All + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + diff --git a/PcgsInv/Program.cs b/PcgsInv/Program.cs new file mode 100644 index 0000000..925aedd --- /dev/null +++ b/PcgsInv/Program.cs @@ -0,0 +1,27 @@ +using System; +using Avalonia; +using ReactiveUI.Avalonia; + +namespace PcgsInv; + +internal sealed class Program { + // Initialization code. Don't use any Avalonia, third-party APIs or any + // SynchronizationContext-reliant code before AppMain is called: things aren't initialized + // yet and stuff might break. + [STAThread] + public static void Main(string[] args) { + BuildAvaloniaApp() + .StartWithClassicDesktopLifetime(args); + } + + // Avalonia configuration, don't remove; also used by visual designer. + public static AppBuilder BuildAvaloniaApp() + => AppBuilder.Configure() + .UsePlatformDetect() + .UseReactiveUI(builder => builder.WithAvalonia()) +#if DEBUG + .WithDeveloperTools() +#endif + .WithInterFont() + .LogToTrace(); +} \ No newline at end of file diff --git a/PcgsInv/ViewLocator.cs b/PcgsInv/ViewLocator.cs new file mode 100644 index 0000000..2dec213 --- /dev/null +++ b/PcgsInv/ViewLocator.cs @@ -0,0 +1,31 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Avalonia.Controls; +using Avalonia.Controls.Templates; +using PcgsInv.ViewModels; + +namespace PcgsInv; + +/// +/// Given a view model, returns the corresponding view if possible. +/// +[RequiresUnreferencedCode( + "Default implementation of ViewLocator involves reflection which may be trimmed away.", + Url = "https://docs.avaloniaui.net/docs/concepts/view-locator")] +public class ViewLocator : IDataTemplate { + public Control? Build(object? param) { + if (param is null) + return null; + + var name = param.GetType().FullName!.Replace("ViewModel", "View", StringComparison.Ordinal); + var type = Type.GetType(name); + + if (type != null) return (Control)Activator.CreateInstance(type)!; + + return new TextBlock { Text = "Not Found: " + name }; + } + + public bool Match(object? data) { + return data is ViewModelBase; + } +} \ No newline at end of file diff --git a/PcgsInv/ViewModels/MainWindowViewModel.cs b/PcgsInv/ViewModels/MainWindowViewModel.cs new file mode 100644 index 0000000..ca2d0e0 --- /dev/null +++ b/PcgsInv/ViewModels/MainWindowViewModel.cs @@ -0,0 +1,30 @@ +using System; +using System.Diagnostics; +using PcgsApi; +using PcgsInv.Models; +using ReactiveUI; +using ReactiveUI.SourceGenerators; + +namespace PcgsInv.ViewModels; + +public partial class MainWindowViewModel : ViewModelBase { + [Reactive] private CoinCollection _collection; + [Reactive] private PcgsCoin? _selectedCoin; + + public MainWindowViewModel() { + Collection = new CoinCollection(); + _selectedCoin = null; + + Collection.PropertyChanged += (s, e) => { + Debug.WriteLine($"Collection raised event: {e.PropertyName}"); + }; + + this.WhenAnyValue(x => x.Collection.TotalValue) + .Subscribe(x => Debug.WriteLine($"Total value was recomputed: {x}")); + } + + [ReactiveCommand] + private void CreateTestingCoin() { + Collection.Coins.Add(new PcgsCoin()); + } +} \ No newline at end of file diff --git a/PcgsInv/ViewModels/ViewModelBase.cs b/PcgsInv/ViewModels/ViewModelBase.cs new file mode 100644 index 0000000..8bdfd8d --- /dev/null +++ b/PcgsInv/ViewModels/ViewModelBase.cs @@ -0,0 +1,5 @@ +using ReactiveUI; + +namespace PcgsInv.ViewModels; + +public abstract class ViewModelBase : ReactiveObject { } \ No newline at end of file diff --git a/PcgsInv/Views/MainWindow.axaml b/PcgsInv/Views/MainWindow.axaml new file mode 100644 index 0000000..d539445 --- /dev/null +++ b/PcgsInv/Views/MainWindow.axaml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + +