From defcc0b3601052e34859c0dd58e7f6dc29fbee4a Mon Sep 17 00:00:00 2001 From: Joseph Moreno <44370115+josephmoresena@users.noreply.github.com> Date: Tue, 7 Feb 2023 23:34:30 -0500 Subject: [PATCH] Avalonia UI 11 Preview 5 * Menu fixes for macOS. * Delete nuget.config. * Nullable enabled. * Fixes in ViewModel class names. * ViewModel for MainControl as MainWindow state. * Preserve window state when swapping Fluent and Simple themes. --- src/App.axaml | 13 +++-- src/App.axaml.cs | 49 ++++++++++--------- src/AvaloniaCoreRTDemo.csproj | 16 +++--- src/Controls/MainControl.axaml | 4 +- src/Controls/MainControl.axaml.cs | 10 +++- .../ViewModels/MainControlViewModel.cs | 49 +++++++++++++++++++ src/Controls/ViewModels/MainViewModel.cs | 31 ------------ src/Interfaces/IMainWindow.cs | 5 +- src/Interfaces/IMainWindowState.cs | 15 ++++++ src/Windows/AboutWindow.axaml.cs | 10 ++-- src/Windows/MainWindow.axaml | 4 +- src/Windows/MainWindow.axaml.cs | 20 +++++--- src/Windows/ViewModels/AboutViewModel.cs | 2 +- ...ewModelBase.cs => ApplicationModelBase.cs} | 47 +++++++++++------- src/Windows/ViewModels/MainViewModel.cs | 27 +++------- src/nuget.config | 7 --- 16 files changed, 182 insertions(+), 127 deletions(-) create mode 100644 src/Controls/ViewModels/MainControlViewModel.cs delete mode 100644 src/Controls/ViewModels/MainViewModel.cs create mode 100644 src/Interfaces/IMainWindowState.cs rename src/Windows/ViewModels/{MainViewModelBase.cs => ApplicationModelBase.cs} (68%) delete mode 100644 src/nuget.config diff --git a/src/App.axaml b/src/App.axaml index 65e3c87..bc3aa7a 100644 --- a/src/App.axaml +++ b/src/App.axaml @@ -1,10 +1,17 @@ + xmlns:viewModels1="clr-namespace:AvaloniaCoreRTDemo.Windows.ViewModels" + RequestedThemeVariant="Light" + x:Class="AvaloniaCoreRTDemo.App" + x:CompileBindings="True" + x:DataType="viewModels1:ApplicationModelBase"> + + + + + - - \ No newline at end of file diff --git a/src/App.axaml.cs b/src/App.axaml.cs index e0ead4d..57d31d4 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -1,6 +1,6 @@ - using System; using Avalonia; +using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; using Avalonia.Styling; @@ -13,10 +13,10 @@ namespace AvaloniaCoreRTDemo { public sealed class App : Application, IThemeSwitch { - private FluentTheme _fluentTheme; - private SimpleTheme _simpleTheme; - private IStyle _fluentDataGrid; - private IStyle _simpleDataGrid; + private FluentTheme _fluentTheme = default!; + private SimpleTheme _simpleTheme = default!; + private IStyle _fluentDataGrid = default!; + private IStyle _simpleDataGrid = default!; private ApplicationTheme _currentTheme; @@ -39,13 +39,14 @@ namespace AvaloniaCoreRTDemo private void InitializeThemes() { - this._fluentTheme = (FluentTheme)this.Resources["fluentTheme"]!; - this._simpleTheme = (SimpleTheme)this.Resources["simpleTheme"]!; + this._simpleTheme = new SimpleTheme(); + this._fluentTheme = new FluentTheme(); + this._fluentDataGrid = (IStyle)this.Resources["fluentDataGrid"]!; this._simpleDataGrid = (IStyle)this.Resources["simpleDataGrid"]!; + Styles.Add(_fluentTheme); Styles.Add(_fluentDataGrid); - this._currentTheme = ApplicationTheme.FluentLight; } @@ -53,48 +54,52 @@ namespace AvaloniaCoreRTDemo void IThemeSwitch.ChangeTheme(ApplicationTheme theme) { - var themeChanged = theme switch + if (theme == this._currentTheme) + return; + + Boolean themeChanged = theme switch { - ApplicationTheme.SimpleLight => _currentTheme is ApplicationTheme.FluentDark or ApplicationTheme.FluentLight, - ApplicationTheme.SimpleDark => _currentTheme is ApplicationTheme.FluentDark or ApplicationTheme.FluentLight, - ApplicationTheme.FluentLight => _currentTheme is ApplicationTheme.SimpleLight or ApplicationTheme.SimpleDark, - ApplicationTheme.FluentDark => _currentTheme is ApplicationTheme.SimpleLight or ApplicationTheme.SimpleDark, + ApplicationTheme.SimpleLight => this._currentTheme is ApplicationTheme.FluentDark or ApplicationTheme.FluentLight, + ApplicationTheme.SimpleDark => this._currentTheme is ApplicationTheme.FluentDark or ApplicationTheme.FluentLight, + ApplicationTheme.FluentLight => this._currentTheme is ApplicationTheme.SimpleLight or ApplicationTheme.SimpleDark, + ApplicationTheme.FluentDark => this._currentTheme is ApplicationTheme.SimpleLight or ApplicationTheme.SimpleDark, _ => throw new ArgumentOutOfRangeException(nameof(theme), theme, null) }; - + this._currentTheme = theme; switch (theme) { case ApplicationTheme.SimpleLight: - this._simpleTheme.Mode = SimpleThemeMode.Light; + this.SetValue(ThemeVariantScope.ActualThemeVariantProperty, ThemeVariant.Light); this.Styles[0] = this._simpleTheme; this.Styles[1] = this._simpleDataGrid; break; case ApplicationTheme.SimpleDark: - this._simpleTheme.Mode = SimpleThemeMode.Dark; + this.SetValue(ThemeVariantScope.ActualThemeVariantProperty, ThemeVariant.Dark); this.Styles[0] = this._simpleTheme; this.Styles[1] = this._simpleDataGrid; break; case ApplicationTheme.FluentLight: - this._fluentTheme.Mode = FluentThemeMode.Light; + this.SetValue(ThemeVariantScope.ActualThemeVariantProperty, ThemeVariant.Light); this.Styles[0] = this._fluentTheme; this.Styles[1] = this._fluentDataGrid; break; case ApplicationTheme.FluentDark: - this._fluentTheme.Mode = FluentThemeMode.Dark; + this.SetValue(ThemeVariantScope.ActualThemeVariantProperty, ThemeVariant.Dark); this.Styles[0] = this._fluentTheme; this.Styles[1] = this._fluentDataGrid; break; } - if (themeChanged && ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + if (themeChanged && this.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - var oldWindow = desktop.MainWindow; - var newWindow = new MainWindow(); + MainWindow oldWindow = (desktop.MainWindow as MainWindow)!; + MainWindow newWindow = new MainWindow(oldWindow); + desktop.MainWindow = newWindow; + this.DataContext = newWindow.DataContext; newWindow.Show(); oldWindow.Close(); - this.DataContext = desktop.MainWindow.DataContext; } } } diff --git a/src/AvaloniaCoreRTDemo.csproj b/src/AvaloniaCoreRTDemo.csproj index 3651354..5a9bdfb 100644 --- a/src/AvaloniaCoreRTDemo.csproj +++ b/src/AvaloniaCoreRTDemo.csproj @@ -8,7 +8,7 @@ Assets/app.ico true true - + enable true true true @@ -40,14 +40,14 @@ - - - - - - + + + + + + - + diff --git a/src/Controls/MainControl.axaml b/src/Controls/MainControl.axaml index d443c2d..13c2ab2 100644 --- a/src/Controls/MainControl.axaml +++ b/src/Controls/MainControl.axaml @@ -2,11 +2,11 @@ xmlns:viewModels="clr-namespace:AvaloniaCoreRTDemo.Controls.ViewModels" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="AvaloniaCoreRTDemo.Controls.MainControl" x:CompileBindings="True" - x:DataType="viewModels:MainViewModel"> + x:DataType="viewModels:MainControlViewModel"> Welcome to Avalonia UI + NativeAOT! - + diff --git a/src/Controls/MainControl.axaml.cs b/src/Controls/MainControl.axaml.cs index 59d7185..13496ee 100644 --- a/src/Controls/MainControl.axaml.cs +++ b/src/Controls/MainControl.axaml.cs @@ -9,13 +9,19 @@ namespace AvaloniaCoreRTDemo.Controls { public MainControl() { - InitializeComponent(); + this.InitializeComponent(); } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); - this.DataContext = new MainViewModel(); + this.DataContext = new MainControlViewModel(); + } + + public void Reload(IMainWindowState? model) + { + if(model is not null) + this.DataContext = new MainControlViewModel(model); } } } diff --git a/src/Controls/ViewModels/MainControlViewModel.cs b/src/Controls/ViewModels/MainControlViewModel.cs new file mode 100644 index 0000000..0254d8f --- /dev/null +++ b/src/Controls/ViewModels/MainControlViewModel.cs @@ -0,0 +1,49 @@ +using System; +using System.IO; + +using Avalonia.Media.Imaging; +using AvaloniaCoreRTDemo.Interfaces; +using ReactiveUI; + +namespace AvaloniaCoreRTDemo.Controls.ViewModels +{ + internal sealed class MainControlViewModel : ReactiveObject, IMainWindowState + { + private readonly IBitmap _dotNetImage; + private readonly IBitmap _avaloniaImage; + + private Boolean _unloadable = false; + + public IBitmap DotNetImage => this._dotNetImage; + public IBitmap AvaloniaImage => this._avaloniaImage; + public String? Text { get; set; } + + public MainControlViewModel() + { + this._dotNetImage = Utilities.GetImageFromFile("dotnet.png"); + this._avaloniaImage = Utilities.GetImageFromFile("avalonia.png"); + } + + public MainControlViewModel(IMainWindowState state) + { + this._avaloniaImage = state.AvaloniaImage; + this._dotNetImage = state.DotNetImage; + this.Text = state.Text; + state.SetUnloadable(); + } + + ~MainControlViewModel() + { + if (!this._unloadable) + { + this._dotNetImage.Dispose(); + this._avaloniaImage.Dispose(); + } + } + + void IMainWindowState.SetUnloadable() + { + this._unloadable = true; + } + } +} diff --git a/src/Controls/ViewModels/MainViewModel.cs b/src/Controls/ViewModels/MainViewModel.cs deleted file mode 100644 index f1cea96..0000000 --- a/src/Controls/ViewModels/MainViewModel.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.IO; - -using Avalonia.Media.Imaging; - -using ReactiveUI; - -namespace AvaloniaCoreRTDemo.Controls.ViewModels -{ - internal sealed class MainViewModel : ReactiveObject - { - private readonly IBitmap _dotNetImage; - private readonly IBitmap _avaloniaImage; - - public IBitmap DotNetImage => this._dotNetImage; - - public IBitmap AvaloniaImage => this._avaloniaImage; - - public MainViewModel() - { - this._dotNetImage = Utilities.GetImageFromFile("dotnet.png"); - this._avaloniaImage = Utilities.GetImageFromFile("avalonia.png"); - } - - ~MainViewModel() - { - this._dotNetImage.Dispose(); - this._avaloniaImage.Dispose(); - } - } -} diff --git a/src/Interfaces/IMainWindow.cs b/src/Interfaces/IMainWindow.cs index 2f8b2a8..a670e2f 100644 --- a/src/Interfaces/IMainWindow.cs +++ b/src/Interfaces/IMainWindow.cs @@ -1,9 +1,10 @@ -using AvaloniaCoreRTDemo.Interfaces; +using Avalonia.Controls; -namespace AvaloniaCoreRTDemo +namespace AvaloniaCoreRTDemo.Interfaces { public interface IMainWindow { IThemeSwitch ThemeSwitch { get; } + IMainWindowState Model { get; } } } diff --git a/src/Interfaces/IMainWindowState.cs b/src/Interfaces/IMainWindowState.cs new file mode 100644 index 0000000..752cdac --- /dev/null +++ b/src/Interfaces/IMainWindowState.cs @@ -0,0 +1,15 @@ +using System; +using Avalonia.Media.Imaging; + +namespace AvaloniaCoreRTDemo +{ + public interface IMainWindowState + { + IBitmap DotNetImage { get; } + IBitmap AvaloniaImage { get; } + String? Text { get; } + + void SetUnloadable(); + } +} + diff --git a/src/Windows/AboutWindow.axaml.cs b/src/Windows/AboutWindow.axaml.cs index 4b9ca92..e1b7aa0 100644 --- a/src/Windows/AboutWindow.axaml.cs +++ b/src/Windows/AboutWindow.axaml.cs @@ -1,5 +1,5 @@ using System; - +using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; @@ -9,14 +9,14 @@ namespace AvaloniaCoreRTDemo.Windows { public sealed partial class AboutWindow : Window { - public AboutWindow() - { - this.InitializeComponent(); - } + public AboutWindow() : this(false) { } public AboutWindow(Boolean darkTheme) { this.InitializeComponent(darkTheme); +#if DEBUG + this.AttachDevTools(); +#endif } private void InitializeComponent(Boolean darkTheme = default) diff --git a/src/Windows/MainWindow.axaml b/src/Windows/MainWindow.axaml index e82bf20..4db5a61 100644 --- a/src/Windows/MainWindow.axaml +++ b/src/Windows/MainWindow.axaml @@ -4,10 +4,10 @@ mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" x:Class="AvaloniaCoreRTDemo.Windows.MainWindow" Width="640" Height="480" WindowStartupLocation="CenterScreen" Title="AvaloniaCoreRTDemo" Icon="avares://AvaloniaCoreRTDemo/Assets/app.ico" MinWidth="400" MinHeight="350" x:CompileBindings="True" - x:DataType="viewModels1:MainViewModelBase"> + x:DataType="viewModels1:ApplicationModelBase"> - + diff --git a/src/Windows/MainWindow.axaml.cs b/src/Windows/MainWindow.axaml.cs index a846014..3c56369 100644 --- a/src/Windows/MainWindow.axaml.cs +++ b/src/Windows/MainWindow.axaml.cs @@ -1,7 +1,10 @@ +using System; using Avalonia; using Avalonia.Controls; +using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; - +using AvaloniaCoreRTDemo.Controls; +using AvaloniaCoreRTDemo.Controls.ViewModels; using AvaloniaCoreRTDemo.Interfaces; using AvaloniaCoreRTDemo.Windows.ViewModels; @@ -9,20 +12,25 @@ namespace AvaloniaCoreRTDemo.Windows { public sealed partial class MainWindow : Window, IMainWindow { - public MainWindow() + private readonly Application? _app = App.Current; + + public MainWindow() : this(default) { } + public MainWindow(IMainWindow? window) { - InitializeComponent(); + this.InitializeComponent(window); #if DEBUG this.AttachDevTools(); #endif } - private void InitializeComponent() + IThemeSwitch IMainWindow.ThemeSwitch => (this._app as IThemeSwitch)!; + IMainWindowState IMainWindow.Model => (this.GetControl("mainControl")!.DataContext as IMainWindowState)!; + + private void InitializeComponent(IMainWindow? window) { AvaloniaXamlLoader.Load(this); this.DataContext = new MainViewModel(this); + this.GetControl("mainControl").Reload(window?.Model); } - - IThemeSwitch IMainWindow.ThemeSwitch => App.Current as IThemeSwitch; } } diff --git a/src/Windows/ViewModels/AboutViewModel.cs b/src/Windows/ViewModels/AboutViewModel.cs index 0cb018f..e43bd95 100644 --- a/src/Windows/ViewModels/AboutViewModel.cs +++ b/src/Windows/ViewModels/AboutViewModel.cs @@ -8,7 +8,7 @@ using ReactiveUI; namespace AvaloniaCoreRTDemo.Windows.ViewModels { - internal record SystemDetail(string Key, string Value); + internal record SystemDetail(String Key, String Value); internal sealed class AboutViewModel : ReactiveObject { diff --git a/src/Windows/ViewModels/MainViewModelBase.cs b/src/Windows/ViewModels/ApplicationModelBase.cs similarity index 68% rename from src/Windows/ViewModels/MainViewModelBase.cs rename to src/Windows/ViewModels/ApplicationModelBase.cs index f9d09da..09086e3 100644 --- a/src/Windows/ViewModels/MainViewModelBase.cs +++ b/src/Windows/ViewModels/ApplicationModelBase.cs @@ -8,14 +8,14 @@ using ReactiveUI; namespace AvaloniaCoreRTDemo.Windows.ViewModels { - internal abstract class MainViewModelBase : ReactiveObject + internal abstract class ApplicationModelBase : ReactiveObject { private readonly IThemeSwitch _themeSwitch; private Boolean _aboutEnable = true; - private Boolean _defaultLightEnable = true; - private Boolean _defaultDarkEnable = true; - private Boolean _fluentLightEnable = true; - private Boolean _fluentDarkEnable = true; + private Boolean _defaultLightEnable = false; + private Boolean _defaultDarkEnable = false; + private Boolean _fluentLightEnable = false; + private Boolean _fluentDarkEnable = false; public Boolean AboutEnabled { @@ -23,12 +23,6 @@ namespace AvaloniaCoreRTDemo.Windows.ViewModels set => this.RaiseAndSetIfChanged(ref this._aboutEnable, value); } - public MainViewModelBase(IThemeSwitch window) - { - this._themeSwitch = window; - this.FileExitCommand = ReactiveCommand.Create(RunFileExit); - } - public Boolean DefaultLightEnabled { get => this._defaultLightEnable; @@ -54,17 +48,20 @@ namespace AvaloniaCoreRTDemo.Windows.ViewModels } public ReactiveCommand FileExitCommand { get; } - - public abstract void HelpAboutMethod(); + public ApplicationModelBase(IThemeSwitch themeSwitch) + { + this._themeSwitch = themeSwitch; + this.IntializeTheme(themeSwitch.Current); + this.FileExitCommand = ReactiveCommand.Create(RunFileExit); + } + + public abstract void HelpAboutMethod(); public abstract void DefaultLightMethod(); public abstract void DefaultDarkMethod(); public abstract void FluentLightMethod(); public abstract void FluentDarkMethod(); - - private void RunFileExit() - => Environment.Exit(0); - + protected async void RunHelpAbout(Window currentWindow) { if (this.AboutEnabled) @@ -79,6 +76,22 @@ namespace AvaloniaCoreRTDemo.Windows.ViewModels } } + protected void SetTheme(ApplicationTheme theme) + { + this.IntializeTheme(theme); + this._themeSwitch.ChangeTheme(theme); + } + + private void RunFileExit() => Environment.Exit(0); + + private void IntializeTheme(ApplicationTheme theme) + { + this.DefaultLightEnabled = theme != ApplicationTheme.SimpleLight; + this.DefaultDarkEnabled = theme != ApplicationTheme.SimpleDark; + this.FluentLightEnabled = theme != ApplicationTheme.FluentLight; + this.FluentDarkEnabled = theme != ApplicationTheme.FluentDark; + } + private static Boolean IsDarkTheme(ApplicationTheme? theme) => theme switch { diff --git a/src/Windows/ViewModels/MainViewModel.cs b/src/Windows/ViewModels/MainViewModel.cs index 3bd40b8..bf9d68b 100644 --- a/src/Windows/ViewModels/MainViewModel.cs +++ b/src/Windows/ViewModels/MainViewModel.cs @@ -2,38 +2,27 @@ using System.Reactive; using Avalonia.Controls; - +using AvaloniaCoreRTDemo.Controls.ViewModels; +using AvaloniaCoreRTDemo.Interfaces; using ReactiveUI; namespace AvaloniaCoreRTDemo.Windows.ViewModels { - internal sealed class MainViewModel : MainViewModelBase + internal sealed class MainViewModel : ApplicationModelBase where TWindow : Window, IMainWindow { - private readonly TWindow _window; + private TWindow _window; public MainViewModel(TWindow window) : base(window.ThemeSwitch) { this._window = window; - this.ChangeTheme(window.ThemeSwitch.Current); } - public override void HelpAboutMethod() => base.RunHelpAbout(this._window); - - public override void DefaultLightMethod() => this.ChangeTheme(ApplicationTheme.SimpleLight); - public override void DefaultDarkMethod() => this.ChangeTheme(ApplicationTheme.SimpleDark); - public override void FluentLightMethod() => this.ChangeTheme(ApplicationTheme.FluentLight); - public override void FluentDarkMethod() => this.ChangeTheme(ApplicationTheme.FluentDark); - - private void ChangeTheme(ApplicationTheme theme) - { - this.DefaultLightEnabled = theme != ApplicationTheme.SimpleLight; - this.DefaultDarkEnabled = theme != ApplicationTheme.SimpleDark; - this.FluentLightEnabled = theme != ApplicationTheme.FluentLight; - this.FluentDarkEnabled = theme != ApplicationTheme.FluentDark; - this._window.ThemeSwitch?.ChangeTheme(theme); - } + public override void DefaultLightMethod() => base.SetTheme(ApplicationTheme.SimpleLight); + public override void DefaultDarkMethod() => base.SetTheme(ApplicationTheme.SimpleDark); + public override void FluentLightMethod() => base.SetTheme(ApplicationTheme.FluentLight); + public override void FluentDarkMethod() => base.SetTheme(ApplicationTheme.FluentDark); } } diff --git a/src/nuget.config b/src/nuget.config deleted file mode 100644 index 3d11832..0000000 --- a/src/nuget.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - -