├── .github └── workflows │ └── dotnet.yml ├── .gitignore ├── AvaloniaCoreRTDemo.sln ├── LICENSE ├── README.md ├── src ├── App.axaml ├── App.axaml.cs ├── ApplicationTheme.cs ├── Assets │ ├── about.ico │ ├── app.icns │ └── app.ico ├── AvaloniaCoreRTDemo.csproj ├── Controls │ ├── MainControl.axaml │ ├── MainControl.axaml.cs │ └── ViewModels │ │ └── MainControlViewModel.cs ├── Images │ ├── broken-link.png │ ├── linux.png │ ├── linux_d.png │ ├── macos.png │ ├── macos_d.png │ ├── windows.png │ └── windows_d.png ├── Interfaces │ ├── IMainWindow.cs │ ├── IMainWindowState.cs │ └── IThemeSwitch.cs ├── Program.cs ├── Utilities.cs ├── Windows │ ├── AboutWindow.axaml │ ├── AboutWindow.axaml.cs │ ├── MainWindow.axaml │ ├── MainWindow.axaml.cs │ └── ViewModels │ │ ├── AboutViewModel.cs │ │ ├── ApplicationModelBase.cs │ │ └── MainViewModel.cs ├── avalonia.png ├── dotnet.png └── nuget.config ├── test.cmd ├── test.command ├── test.sh ├── test_arm.sh ├── test_arm64.cmd ├── test_arm64.command ├── test_arm64.sh └── test_x86.cmd /.github/workflows/dotnet.yml: -------------------------------------------------------------------------------- 1 | name: NativeAOT Build 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build-on-linux-x64: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | - name: APT deb822 sources 15 | run: | 16 | if [[ $ImageOS == "ubuntu24" ]]; then 17 | sudo bash -c 'cat > /etc/apt/sources.list.d/ubuntu.sources < /etc/apt/sources.list.d/ubuntu.sources < /etc/apt/sources.list.d/arm64.list < /etc/apt/sources.list.d/ubuntu.sources < /etc/apt/sources.list.d/armhf.list < 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/App.axaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Avalonia; 4 | using Avalonia.Controls; 5 | using Avalonia.Controls.ApplicationLifetimes; 6 | using Avalonia.Markup.Xaml; 7 | using Avalonia.Styling; 8 | using Avalonia.Themes.Fluent; 9 | using Avalonia.Themes.Simple; 10 | 11 | using AvaloniaCoreRTDemo.Interfaces; 12 | using AvaloniaCoreRTDemo.Windows; 13 | 14 | namespace AvaloniaCoreRTDemo 15 | { 16 | public sealed class App : Application, IThemeSwitch 17 | { 18 | private FluentTheme _fluentTheme = default!; 19 | private SimpleTheme _simpleTheme = default!; 20 | private IStyle _fluentDataGrid = default!; 21 | private IStyle _simpleDataGrid = default!; 22 | 23 | private ApplicationTheme _currentTheme; 24 | 25 | public override void Initialize() 26 | { 27 | AvaloniaXamlLoader.Load(this); 28 | this.Name = "AvaloniaCoreRTDemo"; 29 | } 30 | 31 | public override void OnFrameworkInitializationCompleted() 32 | { 33 | this.InitializeThemes(); 34 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) 35 | { 36 | desktop.MainWindow = new MainWindow(); 37 | this.DataContext = desktop.MainWindow.DataContext; 38 | } 39 | base.OnFrameworkInitializationCompleted(); 40 | } 41 | 42 | private void InitializeThemes() 43 | { 44 | this._simpleTheme = new SimpleTheme(); 45 | this._fluentTheme = new FluentTheme(); 46 | 47 | this._fluentDataGrid = (IStyle)this.Resources["fluentDataGrid"]!; 48 | this._simpleDataGrid = (IStyle)this.Resources["simpleDataGrid"]!; 49 | 50 | Styles.Add(_fluentTheme); 51 | Styles.Add(_fluentDataGrid); 52 | this._currentTheme = ApplicationTheme.FluentLight; 53 | } 54 | 55 | ApplicationTheme IThemeSwitch.Current => this._currentTheme; 56 | 57 | void IThemeSwitch.ChangeTheme(ApplicationTheme theme) 58 | { 59 | if (theme == this._currentTheme) 60 | return; 61 | 62 | Boolean themeChanged = theme switch 63 | { 64 | ApplicationTheme.SimpleLight => this._currentTheme is ApplicationTheme.FluentDark or ApplicationTheme.FluentLight, 65 | ApplicationTheme.SimpleDark => this._currentTheme is ApplicationTheme.FluentDark or ApplicationTheme.FluentLight, 66 | ApplicationTheme.FluentLight => this._currentTheme is ApplicationTheme.SimpleLight or ApplicationTheme.SimpleDark, 67 | ApplicationTheme.FluentDark => this._currentTheme is ApplicationTheme.SimpleLight or ApplicationTheme.SimpleDark, 68 | _ => throw new ArgumentOutOfRangeException(nameof(theme), theme, null) 69 | }; 70 | 71 | this._currentTheme = theme; 72 | switch (theme) 73 | { 74 | case ApplicationTheme.SimpleLight: 75 | this.SetValue(ThemeVariantScope.ActualThemeVariantProperty, ThemeVariant.Light); 76 | this.Styles[0] = this._simpleTheme; 77 | this.Styles[1] = this._simpleDataGrid; 78 | break; 79 | case ApplicationTheme.SimpleDark: 80 | this.SetValue(ThemeVariantScope.ActualThemeVariantProperty, ThemeVariant.Dark); 81 | this.Styles[0] = this._simpleTheme; 82 | this.Styles[1] = this._simpleDataGrid; 83 | break; 84 | case ApplicationTheme.FluentLight: 85 | this.SetValue(ThemeVariantScope.ActualThemeVariantProperty, ThemeVariant.Light); 86 | this.Styles[0] = this._fluentTheme; 87 | this.Styles[1] = this._fluentDataGrid; 88 | break; 89 | case ApplicationTheme.FluentDark: 90 | this.SetValue(ThemeVariantScope.ActualThemeVariantProperty, ThemeVariant.Dark); 91 | this.Styles[0] = this._fluentTheme; 92 | this.Styles[1] = this._fluentDataGrid; 93 | break; 94 | } 95 | 96 | if (themeChanged && this.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) 97 | { 98 | MainWindow oldWindow = (desktop.MainWindow as MainWindow)!; 99 | MainWindow newWindow = new(oldWindow); 100 | 101 | desktop.MainWindow = newWindow; 102 | this.DataContext = newWindow.DataContext; 103 | 104 | oldWindow.Hide(); 105 | newWindow.Show(); 106 | oldWindow.Close(); 107 | } 108 | } 109 | } 110 | } -------------------------------------------------------------------------------- /src/ApplicationTheme.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace AvaloniaCoreRTDemo 4 | { 5 | public enum ApplicationTheme : Byte 6 | { 7 | SimpleLight = 0, 8 | SimpleDark = 1, 9 | FluentLight = 2, 10 | FluentDark = 3, 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/Assets/about.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teobugslayer/AvaloniaCoreRTDemo/cba7741cded13a0c65b429b028f33d933636dbb9/src/Assets/about.ico -------------------------------------------------------------------------------- /src/Assets/app.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teobugslayer/AvaloniaCoreRTDemo/cba7741cded13a0c65b429b028f33d933636dbb9/src/Assets/app.icns -------------------------------------------------------------------------------- /src/Assets/app.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teobugslayer/AvaloniaCoreRTDemo/cba7741cded13a0c65b429b028f33d933636dbb9/src/Assets/app.ico -------------------------------------------------------------------------------- /src/AvaloniaCoreRTDemo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | WinExe 6 | Exe 7 | net9.0 8 | Assets/app.ico 9 | true 10 | enable 11 | true 12 | true 13 | true 14 | 15 | 16 | 17 | true 18 | true 19 | true 20 | 21 | 22 | 23 | link 24 | 25 | 26 | false 27 | false 28 | true 29 | true 30 | 31 | 32 | 33 | 34 | 35 | 36 | PreserveNewest 37 | 38 | 39 | PreserveNewest 40 | PreserveNewest 41 | 42 | 43 | PreserveNewest 44 | PreserveNewest 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | $(AssemblyName) 65 | $(AssemblyName) 66 | com.$(username).$(AssemblyName) 67 | 1.0.0 68 | APPL 69 | 70 | $(AssemblyName) 71 | Assets/app.icns 72 | NSApplication 73 | true 74 | 1.0 75 | 76 | true 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /src/Controls/MainControl.axaml: -------------------------------------------------------------------------------- 1 | 6 | 7 | Welcome to Avalonia UI + NativeAOT! 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/Controls/MainControl.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | using AvaloniaCoreRTDemo.Controls.ViewModels; 4 | 5 | namespace AvaloniaCoreRTDemo.Controls 6 | { 7 | public sealed partial class MainControl : UserControl 8 | { 9 | public MainControl() 10 | { 11 | this.InitializeComponent(); 12 | } 13 | 14 | private void InitializeComponent() 15 | { 16 | //Use generated InitializeComponent method. 17 | this.InitializeComponent(loadXaml: true); 18 | this.DataContext = new MainControlViewModel(); 19 | } 20 | 21 | public void Reload(IMainWindowState model) 22 | { 23 | this.DataContext = new MainControlViewModel(model); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Controls/ViewModels/MainControlViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Avalonia.Media; 4 | 5 | using ReactiveUI; 6 | 7 | namespace AvaloniaCoreRTDemo.Controls.ViewModels 8 | { 9 | internal sealed class MainControlViewModel : ReactiveObject, IMainWindowState 10 | { 11 | private readonly IImage _dotNetImage; 12 | private readonly IImage _avaloniaImage; 13 | 14 | private Boolean _unloadable = false; 15 | 16 | public IImage DotNetImage => this._dotNetImage; 17 | public IImage AvaloniaImage => this._avaloniaImage; 18 | public String? Text { get; set; } 19 | 20 | public MainControlViewModel() 21 | { 22 | this._dotNetImage = Utilities.GetImageFromFile("dotnet.png"); 23 | this._avaloniaImage = Utilities.GetImageFromFile("avalonia.png"); 24 | } 25 | 26 | public MainControlViewModel(IMainWindowState state) 27 | { 28 | this._avaloniaImage = state.AvaloniaImage; 29 | this._dotNetImage = state.DotNetImage; 30 | this.Text = state.Text; 31 | state.SetUnloadable(); 32 | } 33 | 34 | ~MainControlViewModel() 35 | { 36 | if (!this._unloadable) 37 | { 38 | (this._dotNetImage as IDisposable)?.Dispose(); 39 | (this._avaloniaImage as IDisposable)?.Dispose(); 40 | } 41 | } 42 | 43 | void IMainWindowState.SetUnloadable() 44 | { 45 | this._unloadable = true; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Images/broken-link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teobugslayer/AvaloniaCoreRTDemo/cba7741cded13a0c65b429b028f33d933636dbb9/src/Images/broken-link.png -------------------------------------------------------------------------------- /src/Images/linux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teobugslayer/AvaloniaCoreRTDemo/cba7741cded13a0c65b429b028f33d933636dbb9/src/Images/linux.png -------------------------------------------------------------------------------- /src/Images/linux_d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teobugslayer/AvaloniaCoreRTDemo/cba7741cded13a0c65b429b028f33d933636dbb9/src/Images/linux_d.png -------------------------------------------------------------------------------- /src/Images/macos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teobugslayer/AvaloniaCoreRTDemo/cba7741cded13a0c65b429b028f33d933636dbb9/src/Images/macos.png -------------------------------------------------------------------------------- /src/Images/macos_d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teobugslayer/AvaloniaCoreRTDemo/cba7741cded13a0c65b429b028f33d933636dbb9/src/Images/macos_d.png -------------------------------------------------------------------------------- /src/Images/windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teobugslayer/AvaloniaCoreRTDemo/cba7741cded13a0c65b429b028f33d933636dbb9/src/Images/windows.png -------------------------------------------------------------------------------- /src/Images/windows_d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teobugslayer/AvaloniaCoreRTDemo/cba7741cded13a0c65b429b028f33d933636dbb9/src/Images/windows_d.png -------------------------------------------------------------------------------- /src/Interfaces/IMainWindow.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Avalonia; 4 | using Avalonia.Controls; 5 | 6 | namespace AvaloniaCoreRTDemo.Interfaces 7 | { 8 | public interface IMainWindow 9 | { 10 | IThemeSwitch ThemeSwitch { get; } 11 | IMainWindowState Model { get; } 12 | PixelPoint Position { get; } 13 | Size ClientSize { get; } 14 | Size? FrameSize { get; } 15 | WindowState State { get; } 16 | Double Height { get; } 17 | Double Width { get; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/Interfaces/IMainWindowState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Avalonia.Media; 4 | 5 | namespace AvaloniaCoreRTDemo 6 | { 7 | public interface IMainWindowState 8 | { 9 | IImage DotNetImage { get; } 10 | IImage AvaloniaImage { get; } 11 | String? Text { get; } 12 | 13 | void SetUnloadable(); 14 | } 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/Interfaces/IThemeSwitch.cs: -------------------------------------------------------------------------------- 1 | namespace AvaloniaCoreRTDemo.Interfaces 2 | { 3 | public interface IThemeSwitch 4 | { 5 | ApplicationTheme Current { get; } 6 | void ChangeTheme(ApplicationTheme theme); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | 3 | namespace AvaloniaCoreRTDemo 4 | { 5 | public static class Program 6 | { 7 | // Initialization code. Don't use any Avalonia, third-party APIs or any 8 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 9 | // yet and stuff might break. 10 | public static void Main(string[] args) => BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); 11 | 12 | // Avalonia configuration, don't remove; also used by visual designer. 13 | public static AppBuilder BuildAvaloniaApp() 14 | => AppBuilder.Configure().UsePlatformDetect() 15 | .LogToTrace(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/Utilities.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | using System.Runtime.InteropServices; 5 | 6 | using Avalonia; 7 | using Avalonia.Controls; 8 | using Avalonia.Media.Imaging; 9 | using Avalonia.Platform; 10 | 11 | namespace AvaloniaCoreRTDemo 12 | { 13 | internal static class Utilities 14 | { 15 | private const BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic; 16 | 17 | public static readonly Boolean IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); 18 | public static readonly Boolean IsOSX = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); 19 | public static readonly Boolean IsLinux = RuntimeInformation.IsOSPlatform(OSPlatform.Linux); 20 | 21 | public static Bitmap GetImageFromResources(String fileName) 22 | { 23 | Uri resourceUri = new($"avares://AvaloniaCoreRTDemo/Images/{fileName}"); 24 | return new Bitmap(AssetLoader.Open(resourceUri)); 25 | } 26 | 27 | public static PixelPoint GetWindowPosition(Window window) 28 | { 29 | PixelPoint result = window.Position; 30 | return result; 31 | } 32 | 33 | public static Bitmap GetImageFromFile(String path) 34 | { 35 | try 36 | { 37 | return new Bitmap(GetImageFullPath(path)); 38 | } 39 | catch (Exception) 40 | { 41 | return GetImageFromResources("broken-link.png"); 42 | } 43 | } 44 | 45 | private static String GetImageFullPath(String fileName) 46 | => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Windows/AboutWindow.axaml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/Windows/AboutWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Avalonia.Controls; 4 | 5 | using AvaloniaCoreRTDemo.Windows.ViewModels; 6 | 7 | namespace AvaloniaCoreRTDemo.Windows 8 | { 9 | public sealed partial class AboutWindow : Window 10 | { 11 | private readonly Boolean _darkTheme; 12 | 13 | public AboutWindow() : this(false) { } 14 | 15 | public AboutWindow(Boolean darkTheme) 16 | { 17 | this._darkTheme = darkTheme; 18 | this.InitializeComponent(); 19 | } 20 | 21 | private void InitializeComponent() 22 | { 23 | //Use generated InitializeComponent method. 24 | this.InitializeComponent(loadXaml: true); 25 | this.DataContext = new AboutViewModel(this._darkTheme); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/Windows/MainWindow.axaml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/Windows/MainWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Avalonia; 4 | using Avalonia.Controls; 5 | using Avalonia.Interactivity; 6 | 7 | using AvaloniaCoreRTDemo.Interfaces; 8 | using AvaloniaCoreRTDemo.Windows.ViewModels; 9 | 10 | namespace AvaloniaCoreRTDemo.Windows 11 | { 12 | public sealed partial class MainWindow : Window, IMainWindow 13 | { 14 | private static readonly IThemeSwitch themeSwitch = (IThemeSwitch)App.Current!; 15 | 16 | private readonly WindowState? _initialState; 17 | 18 | public MainWindow() : this(default) { } 19 | 20 | public MainWindow(IMainWindow? window) 21 | { 22 | this._initialState = window?.State; 23 | this.InitializeComponent(window); 24 | } 25 | 26 | IThemeSwitch IMainWindow.ThemeSwitch => themeSwitch; 27 | IMainWindowState IMainWindow.Model => (IMainWindowState)this.MainControl.DataContext!; 28 | PixelPoint IMainWindow.Position => Utilities.GetWindowPosition(this); 29 | Size IMainWindow.ClientSize => this.ClientSize; 30 | Size? IMainWindow.FrameSize => this.FrameSize; 31 | WindowState IMainWindow.State => this.WindowState; 32 | 33 | protected override void OnLoaded(RoutedEventArgs e) 34 | { 35 | base.OnLoaded(e); 36 | // The window state on Linux seems to have to be initialized after 37 | // window loading. 38 | if (this._initialState.HasValue && Utilities.IsLinux) 39 | this.WindowState = this._initialState.Value; 40 | } 41 | 42 | private void InitializeComponent(IMainWindow? window) 43 | { 44 | //Use generated InitializeComponent method. 45 | this.InitializeComponent(loadXaml: true); 46 | this.DataContext = new MainViewModel(this); 47 | this.InitializeMenu(); 48 | if (window is not null) 49 | { 50 | this.MainControl.Reload(window.Model); 51 | this.SetSizeAndPosition(window); 52 | } 53 | } 54 | private void InitializeMenu() 55 | { 56 | NativeMenu menu = (NativeMenu)this[NativeMenu.MenuProperty]!; 57 | DisableCurrentTheme(menu, themeSwitch.Current); 58 | if (Utilities.IsOSX) 59 | RemoveAboutMenu(menu); 60 | } 61 | private void SetSizeAndPosition(IMainWindow window) 62 | { 63 | if (!Utilities.IsLinux) 64 | this.WindowState = window.State; 65 | if (this.WindowState == WindowState.Normal) 66 | { 67 | this.WindowStartupLocation = WindowStartupLocation.Manual; 68 | this.Position = window.Position; 69 | this.FrameSize = window.FrameSize; 70 | this.ClientSize = window.ClientSize; 71 | this.Height = window.Height; 72 | this.Width = window.Width; 73 | } 74 | } 75 | 76 | private static void DisableCurrentTheme(NativeMenu menu, ApplicationTheme theme) 77 | { 78 | NativeMenuItem themeMenu = (NativeMenuItem)menu.Items[1]; 79 | NativeMenuItem themeItem = (NativeMenuItem)themeMenu.Menu!.Items[(Int32)theme]; 80 | themeItem.IsEnabled = false; 81 | } 82 | private static void RemoveAboutMenu(NativeMenu menu) 83 | { 84 | NativeMenuItem fileMenu = (NativeMenuItem)menu.Items[0]; 85 | fileMenu.Menu!.Items.RemoveAt(1); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Windows/ViewModels/AboutViewModel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.InteropServices; 4 | 5 | using Avalonia.Media; 6 | 7 | using ReactiveUI; 8 | 9 | namespace AvaloniaCoreRTDemo.Windows.ViewModels 10 | { 11 | internal record SystemDetail(string Key, string Value); 12 | 13 | internal sealed class AboutViewModel : ReactiveObject 14 | { 15 | private readonly IImage _computerImage; 16 | private readonly Boolean _darkTheme; 17 | 18 | public IImage ComputerImage => _computerImage; 19 | 20 | public IReadOnlyList SystemDetails { get; } = new[] 21 | { 22 | new SystemDetail("Number of Cores", Environment.ProcessorCount.ToString()), 23 | new SystemDetail("OS", RuntimeInformation.OSDescription), 24 | new SystemDetail("OS Arch", RuntimeInformation.OSArchitecture.ToString()), 25 | new SystemDetail("OS Version", Environment.OSVersion.ToString()), 26 | new SystemDetail("Computer", Environment.MachineName), 27 | new SystemDetail("User", Environment.UserName), 28 | new SystemDetail("System Path", Environment.SystemDirectory), 29 | new SystemDetail("Current Path", Environment.CurrentDirectory), 30 | new SystemDetail("Process Arch", RuntimeInformation.ProcessArchitecture.ToString()), 31 | new SystemDetail("Runtime Name", RuntimeInformation.FrameworkDescription), 32 | new SystemDetail("Runtime Path", RuntimeEnvironment.GetRuntimeDirectory()), 33 | new SystemDetail("Runtime Version", RuntimeEnvironment.GetSystemVersion()), 34 | new SystemDetail("Framework Version", Environment.Version.ToString()), 35 | }; 36 | 37 | private String ComputerImageName 38 | { 39 | get 40 | { 41 | if (Utilities.IsWindows) 42 | return !this._darkTheme ? "windows.png" : "windows_d.png"; 43 | else if (Utilities.IsOSX) 44 | return !this._darkTheme ? "macos.png" : "macos_d.png"; 45 | else 46 | return !this._darkTheme ? "linux.png" : "linux_d.png"; 47 | } 48 | } 49 | 50 | public AboutViewModel(Boolean darkTheme) 51 | { 52 | this._darkTheme = darkTheme; 53 | this._computerImage = Utilities.GetImageFromResources(this.ComputerImageName); 54 | } 55 | 56 | ~AboutViewModel() 57 | { 58 | (this._computerImage as IDisposable)?.Dispose(); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Windows/ViewModels/ApplicationModelBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reactive; 3 | 4 | using Avalonia.Controls; 5 | 6 | using AvaloniaCoreRTDemo.Interfaces; 7 | 8 | using ReactiveUI; 9 | 10 | namespace AvaloniaCoreRTDemo.Windows.ViewModels 11 | { 12 | internal abstract class ApplicationModelBase : ReactiveObject 13 | { 14 | private readonly IThemeSwitch _themeSwitch; 15 | private Boolean _aboutEnable = true; 16 | private Boolean _defaultLightEnable = false; 17 | private Boolean _defaultDarkEnable = false; 18 | private Boolean _fluentLightEnable = false; 19 | private Boolean _fluentDarkEnable = false; 20 | 21 | public Boolean AboutEnabled 22 | { 23 | get => this._aboutEnable; 24 | set => this.RaiseAndSetIfChanged(ref this._aboutEnable, value); 25 | } 26 | 27 | public Boolean DefaultLightEnabled 28 | { 29 | get => this._defaultLightEnable; 30 | set => this.RaiseAndSetIfChanged(ref this._defaultLightEnable, value); 31 | } 32 | 33 | public Boolean DefaultDarkEnabled 34 | { 35 | get => this._defaultDarkEnable; 36 | set => this.RaiseAndSetIfChanged(ref this._defaultDarkEnable, value); 37 | } 38 | 39 | public Boolean FluentLightEnabled 40 | { 41 | get => this._fluentLightEnable; 42 | set => this.RaiseAndSetIfChanged(ref this._fluentLightEnable, value); 43 | } 44 | 45 | public Boolean FluentDarkEnabled 46 | { 47 | get => this._fluentDarkEnable; 48 | set => this.RaiseAndSetIfChanged(ref this._fluentDarkEnable, value); 49 | } 50 | 51 | public ReactiveCommand FileExitCommand { get; } 52 | 53 | public ApplicationModelBase(IThemeSwitch themeSwitch) 54 | { 55 | this._themeSwitch = themeSwitch; 56 | this.IntializeTheme(themeSwitch.Current); 57 | this.FileExitCommand = ReactiveCommand.Create(RunFileExit); 58 | } 59 | 60 | public abstract void HelpAboutMethod(); 61 | public abstract void DefaultLightMethod(); 62 | public abstract void DefaultDarkMethod(); 63 | public abstract void FluentLightMethod(); 64 | public abstract void FluentDarkMethod(); 65 | 66 | protected async void RunHelpAbout(Window currentWindow) 67 | { 68 | if (this.AboutEnabled) 69 | try 70 | { 71 | this.AboutEnabled = false; 72 | await new AboutWindow(IsDarkTheme(this._themeSwitch.Current)).ShowDialog(currentWindow); 73 | } 74 | finally 75 | { 76 | this.AboutEnabled = true; 77 | } 78 | } 79 | 80 | protected void SetTheme(ApplicationTheme theme) 81 | { 82 | this.IntializeTheme(theme); 83 | this._themeSwitch.ChangeTheme(theme); 84 | } 85 | 86 | private void RunFileExit() => Environment.Exit(0); 87 | 88 | private void IntializeTheme(ApplicationTheme theme) 89 | { 90 | this.DefaultLightEnabled = theme != ApplicationTheme.SimpleLight; 91 | this.DefaultDarkEnabled = theme != ApplicationTheme.SimpleDark; 92 | this.FluentLightEnabled = theme != ApplicationTheme.FluentLight; 93 | this.FluentDarkEnabled = theme != ApplicationTheme.FluentDark; 94 | } 95 | 96 | private static Boolean IsDarkTheme(ApplicationTheme? theme) 97 | => theme switch 98 | { 99 | ApplicationTheme.SimpleDark => true, 100 | ApplicationTheme.FluentDark => true, 101 | _ => false, 102 | }; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/Windows/ViewModels/MainViewModel.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | 3 | using AvaloniaCoreRTDemo.Interfaces; 4 | 5 | namespace AvaloniaCoreRTDemo.Windows.ViewModels 6 | { 7 | internal sealed class MainViewModel : ApplicationModelBase 8 | where TWindow : Window, IMainWindow 9 | { 10 | private TWindow _window; 11 | 12 | public MainViewModel(TWindow window) 13 | : base(window.ThemeSwitch) 14 | { 15 | this._window = window; 16 | } 17 | 18 | public override void HelpAboutMethod() => base.RunHelpAbout(this._window); 19 | public override void DefaultLightMethod() => base.SetTheme(ApplicationTheme.SimpleLight); 20 | public override void DefaultDarkMethod() => base.SetTheme(ApplicationTheme.SimpleDark); 21 | public override void FluentLightMethod() => base.SetTheme(ApplicationTheme.FluentLight); 22 | public override void FluentDarkMethod() => base.SetTheme(ApplicationTheme.FluentDark); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/avalonia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teobugslayer/AvaloniaCoreRTDemo/cba7741cded13a0c65b429b028f33d933636dbb9/src/avalonia.png -------------------------------------------------------------------------------- /src/dotnet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/teobugslayer/AvaloniaCoreRTDemo/cba7741cded13a0c65b429b028f33d933636dbb9/src/dotnet.png -------------------------------------------------------------------------------- /src/nuget.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /test.cmd: -------------------------------------------------------------------------------- 1 | del src\packages.lock.json 2 | dotnet publish -r win-x64 -c Release /p:RestoreLockedMode=true -------------------------------------------------------------------------------- /test.command: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | dir=${0%/*} 3 | if [ -d "$dir" ]; then 4 | cd "$dir" 5 | fi 6 | rm -f src/packages.lock.json 7 | dotnet publish -r osx-x64 -c Release /p:RestoreLockedMode=true -t:BundleApp 8 | rm -rf src/bin/Release/net8.0/osx-x64/publish/Assets/ 9 | rm -rf src/bin/Release/net8.0/osx-x64/publish/AvaloniaCoreRTDemo.app/Contents/MacOS/Assets/ 10 | rm -rf src/bin/Release/net8.0/osx-x64/publish/AvaloniaCoreRTDemo.app/Contents/MacOS/AvaloniaCoreRTDemo.dsym -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -f src/packages.lock.json 3 | dotnet publish -r linux-x64 -c Release /p:RestoreLockedMode=true 4 | cd src/bin/Release/net9.0/linux-x64/publish 5 | mv AvaloniaCoreRTDemo AvaloniaCoreRTDemo.bin 6 | -------------------------------------------------------------------------------- /test_arm.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -f src/packages.lock.json 3 | dotnet publish -r linux-arm -c Release /p:RestoreLockedMode=true /p:LinkerFlavor=lld /p:ObjCopyName=/usr/arm-linux-gnueabihf/bin/objcopy 4 | cd src/bin/Release/net9.0/linux-arm/publish 5 | mv AvaloniaCoreRTDemo AvaloniaCoreRTDemo.bin 6 | -------------------------------------------------------------------------------- /test_arm64.cmd: -------------------------------------------------------------------------------- 1 | del src\packages.lock.json 2 | dotnet publish -r win-arm64 -c Release /p:RestoreLockedMode=true -------------------------------------------------------------------------------- /test_arm64.command: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | dir=${0%/*} 3 | if [ -d "$dir" ]; then 4 | cd "$dir" 5 | fi 6 | rm -f src/packages.lock.json 7 | dotnet publish -r osx-arm64 -c Release /p:RestoreLockedMode=true -t:BundleApp 8 | rm -rf src/bin/Release/net8.0/osx-arm64/publish/Assets/ 9 | rm -rf src/bin/Release/net8.0/osx-arm64/publish/AvaloniaCoreRTDemo.app/Contents/MacOS/Assets/ 10 | rm -rf src/bin/Release/net8.0/osx-arm64/publish/AvaloniaCoreRTDemo.app/Contents/MacOS/AvaloniaCoreRTDemo.dsym -------------------------------------------------------------------------------- /test_arm64.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -f src/packages.lock.json 3 | dotnet publish -r linux-arm64 -c Release /p:RestoreLockedMode=true 4 | cd src/bin/Release/net9.0/linux-arm64/publish 5 | mv AvaloniaCoreRTDemo AvaloniaCoreRTDemo.bin 6 | -------------------------------------------------------------------------------- /test_x86.cmd: -------------------------------------------------------------------------------- 1 | del src\packages.lock.json 2 | dotnet publish -r win-x86 -c Release /p:RestoreLockedMode=true --------------------------------------------------------------------------------