From 1a6741f9eea281cad7ac0b4b62971ae3316eca6d Mon Sep 17 00:00:00 2001 From: Joseph Moreno <44370115+josephmoresena@users.noreply.github.com> Date: Sat, 11 Dec 2021 21:39:55 -0500 Subject: [PATCH 1/8] Sample improves * UserControl sample. * Multi-theme support. * NativeMenu on macOS. * GitHub workflow for Windows, Linux and MacOS artifact compilation. --- .github/workflows/dotnet.yml | 66 +++++++++ AvaloniaCoreRTDemo.sln | 12 +- src/AboutWindow.xaml | 120 ----------------- src/AboutWindow.xaml.cs | 20 --- src/AboutWindowViewModel.cs | 64 --------- src/App.axaml | 18 +++ src/App.axaml.cs | 87 ++++++++++++ src/App.xaml | 8 -- src/App.xaml.cs | 24 ---- src/ApplicationTheme.cs | 12 ++ src/Assets/about.ico | Bin 0 -> 9662 bytes src/{ => Assets}/app.icns | Bin src/{ => Assets}/app.ico | Bin src/AvaloniaCoreRTDemo.csproj | 125 +++++++++++------- src/Controls/MainControl.axaml | 9 ++ .../MainControl.axaml.cs} | 14 +- src/Controls/ViewModels/MainViewModel.cs | 34 +++++ src/{ => Images}/linux.png | Bin src/Images/linux_d.png | Bin 0 -> 9021 bytes src/{ => Images}/macos.png | Bin src/Images/macos_d.png | Bin 0 -> 6545 bytes src/{ => Images}/windows.png | Bin src/Images/windows_d.png | Bin 0 -> 2152 bytes src/Interfaces/IMainWindow.cs | 9 ++ src/Interfaces/IThemeSwitch.cs | 8 ++ src/MainWindow.xaml | 29 ---- src/MainWindowViewModel.cs | 50 ------- src/Program.cs | 14 +- src/Windows/AboutWindow.axaml | 64 +++++++++ src/Windows/AboutWindow.axaml.cs | 28 ++++ src/Windows/MainWindow.axaml | 21 +++ src/Windows/MainWindow.axaml.cs | 28 ++++ src/Windows/MainWindowMacOS.axaml | 26 ++++ src/Windows/MainWindowMacOS.axaml.cs | 28 ++++ src/Windows/ViewModels/AboutViewModel.cs | 68 ++++++++++ src/Windows/ViewModels/MainViewModel.cs | 72 ++++++++++ src/Windows/ViewModels/MainViewModelBase.cs | 51 +++++++ src/nuget.config | 6 +- src/rd.xml | 3 + test.cmd | 2 +- test.command | 22 +-- test.sh | 8 +- 42 files changed, 751 insertions(+), 399 deletions(-) create mode 100644 .github/workflows/dotnet.yml delete mode 100644 src/AboutWindow.xaml delete mode 100644 src/AboutWindow.xaml.cs delete mode 100644 src/AboutWindowViewModel.cs create mode 100644 src/App.axaml create mode 100644 src/App.axaml.cs delete mode 100644 src/App.xaml delete mode 100644 src/App.xaml.cs create mode 100644 src/ApplicationTheme.cs create mode 100644 src/Assets/about.ico rename src/{ => Assets}/app.icns (100%) rename src/{ => Assets}/app.ico (100%) create mode 100644 src/Controls/MainControl.axaml rename src/{MainWindow.xaml.cs => Controls/MainControl.axaml.cs} (50%) create mode 100644 src/Controls/ViewModels/MainViewModel.cs rename src/{ => Images}/linux.png (100%) create mode 100644 src/Images/linux_d.png rename src/{ => Images}/macos.png (100%) create mode 100644 src/Images/macos_d.png rename src/{ => Images}/windows.png (100%) create mode 100644 src/Images/windows_d.png create mode 100644 src/Interfaces/IMainWindow.cs create mode 100644 src/Interfaces/IThemeSwitch.cs delete mode 100644 src/MainWindow.xaml delete mode 100644 src/MainWindowViewModel.cs create mode 100644 src/Windows/AboutWindow.axaml create mode 100644 src/Windows/AboutWindow.axaml.cs create mode 100644 src/Windows/MainWindow.axaml create mode 100644 src/Windows/MainWindow.axaml.cs create mode 100644 src/Windows/MainWindowMacOS.axaml create mode 100644 src/Windows/MainWindowMacOS.axaml.cs create mode 100644 src/Windows/ViewModels/AboutViewModel.cs create mode 100644 src/Windows/ViewModels/MainViewModel.cs create mode 100644 src/Windows/ViewModels/MainViewModelBase.cs diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml new file mode 100644 index 0000000..e569ecc --- /dev/null +++ b/.github/workflows/dotnet.yml @@ -0,0 +1,66 @@ +name: NativeAOT Build + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build-on-linux: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup NativeAOT pre-requisites + run: sudo apt-get install clang zlib1g-dev libkrb5-dev --assume-yes + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 6.0.x + - name: Publish + run: | + sudo chmod +x ./test.sh + ./test.sh + - uses: actions/upload-artifact@v2 + with: + name: Linux-Artifact + path: | + ./src/bin/x64/Release/net6.0/linux-x64/publish/*.bin + ./src/bin/x64/Release/net6.0/linux-x64/publish/*.so + ./src/bin/x64/Release/net6.0/linux-x64/publish/*.png + build-on-windows: + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 6.0.x + - name: Publish + run: ./test.cmd + - uses: actions/upload-artifact@v2 + with: + name: Windows-Artifact + path: | + .\src\bin\x64\Release\net6.0\win-x64\publish\*.exe + .\src\bin\x64\Release\net6.0\win-x64\publish\*.dll + .\src\bin\x64\Release\net6.0\win-x64\publish\*.png + build-on-macos: + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + - name: Setup .NET + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 6.0.x + - name: Publish + run: | + sudo chmod +x ./test.command + ./test.command + cd ./src/bin/x64/Release/net6.0/osx-x64/publish + zip -r -0 macOS-Artifact.zip *.app + mv *.zip ../../../../../../../. + - uses: actions/upload-artifact@v2 + with: + name: macOS-Artifact + path: macOS-Artifact.zip diff --git a/AvaloniaCoreRTDemo.sln b/AvaloniaCoreRTDemo.sln index e12cf41..24fd18d 100644 --- a/AvaloniaCoreRTDemo.sln +++ b/AvaloniaCoreRTDemo.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29911.98 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31912.275 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AvaloniaCoreRTDemo", "src\AvaloniaCoreRTDemo.csproj", "{41A52A04-F7D8-4981-9546-DB020E54851D}" EndProject @@ -13,12 +13,12 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {41A52A04-F7D8-4981-9546-DB020E54851D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {41A52A04-F7D8-4981-9546-DB020E54851D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {41A52A04-F7D8-4981-9546-DB020E54851D}.Debug|Any CPU.ActiveCfg = Debug|x64 + {41A52A04-F7D8-4981-9546-DB020E54851D}.Debug|Any CPU.Build.0 = Debug|x64 {41A52A04-F7D8-4981-9546-DB020E54851D}.Debug|x64.ActiveCfg = Debug|x64 {41A52A04-F7D8-4981-9546-DB020E54851D}.Debug|x64.Build.0 = Debug|x64 - {41A52A04-F7D8-4981-9546-DB020E54851D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {41A52A04-F7D8-4981-9546-DB020E54851D}.Release|Any CPU.Build.0 = Release|Any CPU + {41A52A04-F7D8-4981-9546-DB020E54851D}.Release|Any CPU.ActiveCfg = Release|x64 + {41A52A04-F7D8-4981-9546-DB020E54851D}.Release|Any CPU.Build.0 = Release|x64 {41A52A04-F7D8-4981-9546-DB020E54851D}.Release|x64.ActiveCfg = Release|x64 {41A52A04-F7D8-4981-9546-DB020E54851D}.Release|x64.Build.0 = Release|x64 EndGlobalSection diff --git a/src/AboutWindow.xaml b/src/AboutWindow.xaml deleted file mode 100644 index 14a606e..0000000 --- a/src/AboutWindow.xaml +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/AboutWindow.xaml.cs b/src/AboutWindow.xaml.cs deleted file mode 100644 index e749cf2..0000000 --- a/src/AboutWindow.xaml.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Avalonia; -using Avalonia.Controls; -using Avalonia.Markup.Xaml; - -namespace AvaloniaCoreRTDemo -{ - public class AboutWindow : Window - { - public AboutWindow() - { - this.InitializeComponent(); - this.DataContext = new AboutWindowViewModel(); - } - - private void InitializeComponent() - { - AvaloniaXamlLoader.Load(this); - } - } -} diff --git a/src/AboutWindowViewModel.cs b/src/AboutWindowViewModel.cs deleted file mode 100644 index 424a18d..0000000 --- a/src/AboutWindowViewModel.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Runtime.InteropServices; -using Avalonia.Media.Imaging; -using ReactiveUI; - -namespace AvaloniaCoreRTDemo -{ - public class AboutWindowViewModel : ReactiveObject - { - private readonly IBitmap computerImage; - - public IBitmap ComputerImage => computerImage; - public String NCores => Environment.ProcessorCount.ToString(); - public String OS => RuntimeInformation.OSDescription; - public String OSArch => RuntimeInformation.OSArchitecture.ToString(); - public String OSVersion => Environment.OSVersion.ToString(); - public String ComputerName => Environment.MachineName; - public String UserName => Environment.UserName; - public String SystemPath => Environment.SystemDirectory; - public String CurrentPath => Environment.CurrentDirectory; - public String ProcessArch => RuntimeInformation.ProcessArchitecture.ToString(); - public String RuntimeName => RuntimeInformation.FrameworkDescription; - public String RuntimePath => RuntimeEnvironment.GetRuntimeDirectory(); - public String RuntimeVersion => RuntimeEnvironment.GetSystemVersion(); - public String FrameworkVersion => Environment.Version.ToString(); - - private String ComputerImageName - { - get - { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - return "windows.png"; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - return "macos.png"; - else - return "linux.png"; - } - } - - public AboutWindowViewModel() - { - this.computerImage = GetImageFromResources(this.ComputerImageName); - } - - ~AboutWindowViewModel() - { - this.computerImage.Dispose(); - } - - private static Bitmap GetImageFromResources(String fileName) - { - Assembly asm = Assembly.GetExecutingAssembly(); - String resourceName = asm.GetManifestResourceNames().FirstOrDefault(str => str.EndsWith(fileName)); - if (resourceName != null) - using (Stream a = asm.GetManifestResourceStream(resourceName)) - return new Bitmap(a); - else - return null; - } - } -} diff --git a/src/App.axaml b/src/App.axaml new file mode 100644 index 0000000..e058919 --- /dev/null +++ b/src/App.axaml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/App.axaml.cs b/src/App.axaml.cs new file mode 100644 index 0000000..42529d0 --- /dev/null +++ b/src/App.axaml.cs @@ -0,0 +1,87 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Markup.Xaml; +using Avalonia.Styling; +using Avalonia.Themes.Fluent; + +using AvaloniaCoreRTDemo.Interfaces; +using AvaloniaCoreRTDemo.Windows; + +namespace AvaloniaCoreRTDemo +{ + public sealed class App : Application, IThemeSwitch + { + private IStyle _baseLight; + private IStyle _baseDark; + + private IStyle _fluentLight; + private IStyle _fluentDark; + + private ApplicationTheme _currentTheme; + + public override void Initialize() + { + AvaloniaXamlLoader.Load(this); + this.Name = "AvaloniaCoreRTDemo"; + } + + public override void OnFrameworkInitializationCompleted() + { + this.InitializeThemes(); + if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) + { + desktop.MainWindow = +#if !OSX + new MainWindow(); +#else + new MainWindowMacOS(); +#endif + this.DataContext = desktop.MainWindow.DataContext; + } + base.OnFrameworkInitializationCompleted(); + } + + private void InitializeThemes() + { + this._baseDark = this.Styles[0]; + this._baseLight = this.Styles[1]; + + this.Styles.Remove(this._baseDark); + + this._fluentLight = (FluentTheme)this.Resources["fluentLight"]!; + this._fluentDark = (FluentTheme)this.Resources["fluentDark"]!; + + this._currentTheme = ApplicationTheme.DefaultLight; + } + + ApplicationTheme IThemeSwitch.Current => this._currentTheme; + + void IThemeSwitch.ChangeTheme(ApplicationTheme theme) + { + this._currentTheme = theme; + switch (theme) + { + case ApplicationTheme.DefaultLight: + this.Styles[0] = this._baseLight; + this.Styles.Remove(this._baseDark); + break; + case ApplicationTheme.DefaultDark: + this.Styles[0] = this._baseDark; + this.Styles.Remove(this._baseLight); + break; + case ApplicationTheme.FluentLight: + this.Styles[0] = this._fluentLight; + this.Styles.Remove(this._fluentDark); + if (!this.Styles.Contains(this._baseLight)) + this.Styles.Add(this._baseLight); + break; + case ApplicationTheme.FluentDark: + this.Styles[0] = this._fluentDark; + this.Styles.Remove(this._baseLight); + if (!this.Styles.Contains(this._baseDark)) + this.Styles.Add(this._baseDark); + break; + } + } + } +} \ No newline at end of file diff --git a/src/App.xaml b/src/App.xaml deleted file mode 100644 index 6029ed9..0000000 --- a/src/App.xaml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - diff --git a/src/App.xaml.cs b/src/App.xaml.cs deleted file mode 100644 index 2610577..0000000 --- a/src/App.xaml.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Avalonia; -using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Markup.Xaml; - -namespace AvaloniaCoreRTDemo -{ - public class App : Application - { - public override void Initialize() - { - AvaloniaXamlLoader.Load(this); - } - - public override void OnFrameworkInitializationCompleted() - { - if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - desktop.MainWindow = new MainWindow(); - } - - base.OnFrameworkInitializationCompleted(); - } - } -} \ No newline at end of file diff --git a/src/ApplicationTheme.cs b/src/ApplicationTheme.cs new file mode 100644 index 0000000..2323998 --- /dev/null +++ b/src/ApplicationTheme.cs @@ -0,0 +1,12 @@ +using System; + +namespace AvaloniaCoreRTDemo +{ + public enum ApplicationTheme : Byte + { + DefaultLight = 0, + DefaultDark = 1, + FluentLight = 2, + FluentDark = 3, + } +} diff --git a/src/Assets/about.ico b/src/Assets/about.ico new file mode 100644 index 0000000000000000000000000000000000000000..b8d8f38726a32f2a88c961498160fa26b25cd698 GIT binary patch literal 9662 zcmeI2T~5M46os!3>JCi&3zT4D;tPojU@PFGe_MbxXl%m{jL*0PMH4ixLY~b4Vl<5R zmKhU^ftdy;GvSs@Pid#&yJt?P4FIr$uSx}QU4-jBfDHiH$A-J0hK=^8OL-!^_S*q* zRciW8IGawykrYX>F}s()M^pZQYxv<0w1!{&0oE|ZA6N~4@>31&nhdS2=9gf_wfUcD+yydGeAy$7Oy(=YPLFlH)$*r-%8e2j_ow>=*IgB0u>3qxrS! zsIM^P@5j-jBPN?%bxis3x1)0p`AJs`!|UMzTi?}qsL$$%Bs#VAU9+>Y1MC1h@ZSzhp3wocex@4$A4*4B8?*s1 fswyogB|!YA$B4A5w6Hw-rRpA&q7UQ>`DCi!FIqtd literal 0 HcmV?d00001 diff --git a/src/app.icns b/src/Assets/app.icns similarity index 100% rename from src/app.icns rename to src/Assets/app.icns diff --git a/src/app.ico b/src/Assets/app.ico similarity index 100% rename from src/app.ico rename to src/Assets/app.ico diff --git a/src/AvaloniaCoreRTDemo.csproj b/src/AvaloniaCoreRTDemo.csproj index a4682c4..26a4b5f 100644 --- a/src/AvaloniaCoreRTDemo.csproj +++ b/src/AvaloniaCoreRTDemo.csproj @@ -1,61 +1,61 @@ - + + - + WinExe - net5.0 - AnyCPU;x64 - app.ico + net6.0 + x64 + Assets/app.ico + true + + link + true + true + true + OSX - + - link false false true - - - + + + - - %(Filename) - - - Designer - - - - - - - - - - - - - - - - - - + + PreserveNewest PreserveNewest - - + + PreserveNewest - + + + + + + + + + + + + + + + $(AssemblyName) $(AssemblyName) com.$(username).$(AssemblyName) @@ -63,18 +63,51 @@ APPL $(AssemblyName) - app.icns + Assets/app.icns NSApplication true 1.0 true - - - - - link - - - - + + + + + + + + + + + + + App.axaml + + + + + + + MainControl.axaml + + + AboutWindow.axaml + + + + + + MainWindow.axaml + + + + + + + + MainWindowMacOS.axaml + + + + + \ No newline at end of file diff --git a/src/Controls/MainControl.axaml b/src/Controls/MainControl.axaml new file mode 100644 index 0000000..cdf5d75 --- /dev/null +++ b/src/Controls/MainControl.axaml @@ -0,0 +1,9 @@ + + + Welcome to Avalonia UI + NativeAOT! + + + + + diff --git a/src/MainWindow.xaml.cs b/src/Controls/MainControl.axaml.cs similarity index 50% rename from src/MainWindow.xaml.cs rename to src/Controls/MainControl.axaml.cs index d170c3e..59d7185 100644 --- a/src/MainWindow.xaml.cs +++ b/src/Controls/MainControl.axaml.cs @@ -1,21 +1,21 @@ -using Avalonia; using Avalonia.Controls; -using Avalonia.Controls.Shapes; using Avalonia.Markup.Xaml; -namespace AvaloniaCoreRTDemo +using AvaloniaCoreRTDemo.Controls.ViewModels; + +namespace AvaloniaCoreRTDemo.Controls { - public class MainWindow : Window + public sealed partial class MainControl : UserControl { - public MainWindow() + public MainControl() { InitializeComponent(); - this.DataContext = new MainWindowViewModel(this); } private void InitializeComponent() { AvaloniaXamlLoader.Load(this); + this.DataContext = new MainViewModel(); } } -} \ No newline at end of file +} diff --git a/src/Controls/ViewModels/MainViewModel.cs b/src/Controls/ViewModels/MainViewModel.cs new file mode 100644 index 0000000..ee69584 --- /dev/null +++ b/src/Controls/ViewModels/MainViewModel.cs @@ -0,0 +1,34 @@ +using System; +using System.IO; +using System.Runtime.CompilerServices; + +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 + { + get { return _dotNetImage; } + set { this.RaiseAndSetIfChanged(ref Unsafe.AsRef(this._dotNetImage), value); } + } + + public IBitmap AvaloniaImage + { + get { return _avaloniaImage; } + set { this.RaiseAndSetIfChanged(ref Unsafe.AsRef(this._avaloniaImage), value); } + } + + public MainViewModel() + { + this._dotNetImage = new Bitmap(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "dotnet.png")); + this._avaloniaImage = new Bitmap(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "avalonia.png")); + } + } +} diff --git a/src/linux.png b/src/Images/linux.png similarity index 100% rename from src/linux.png rename to src/Images/linux.png diff --git a/src/Images/linux_d.png b/src/Images/linux_d.png new file mode 100644 index 0000000000000000000000000000000000000000..1ab908b5d5beb8f14ffced2c54f6ef09311d92ec GIT binary patch literal 9021 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 literal 0 HcmV?d00001 diff --git a/src/macos.png b/src/Images/macos.png similarity index 100% rename from src/macos.png rename to src/Images/macos.png diff --git a/src/Images/macos_d.png b/src/Images/macos_d.png new file mode 100644 index 0000000000000000000000000000000000000000..3166c5dbc3f2aeed2c864f9d77d2520cf869a5c2 GIT binary patch literal 6545 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 Y$67k!8vpCdKNj#rQ3F-{*zE290T8Wa@c;k- literal 0 HcmV?d00001 diff --git a/src/windows.png b/src/Images/windows.png similarity index 100% rename from src/windows.png rename to src/Images/windows.png diff --git a/src/Images/windows_d.png b/src/Images/windows_d.png new file mode 100644 index 0000000000000000000000000000000000000000..a9890c9bd5c16c05ea3b580a6aa6f2e679daa6bb GIT binary patch literal 2152 zcmaJ@`#+TH7JuG%uHH-ntjdOn}E)_48p!nUIz8VCS@ zV$ZU5Q)u-2Arlqyfz?kv3I)Zw+1Y@)e$9CR$U646)|^EDcO^ajTMnyaIG%o5UR#>QoxdP>t8S01j`EM)U@#4J=wU1{Jw7NoOg%>A$Mo3|i>#}{qY_o#RkvkCwMhlKt;k-rn-_a-KU00P9x(djmOH^)mSA6=QUAl@A_kAKKk89UJ*gR%{9+x= zt>c$Eq`l<*eygwZQ4nX{iP37M=XgxqW;VWUix&;7U0Ws)`01dUkpg|IcKau(A6Ex|E2qy!ZT#Eq9e}t-n?cyS>$`a4-ugNHrif3Z>8G=R&0t@== ze5e*gtDP^h*7GfdVb|^w_$VCskQL2s*RsTA0CG0(zN0Cj619im7e^tOy|cH{gV0Kr zRM=?h5XktQUXUVMjaCs0%EvW+d?&eY(K&?z0bz|~h#iG=!+)qBkrX70dslqDNZMU)=-TlrpXRp^Gg=!HkpUZl^s5f)1${Ber8><*9w$7P&EGeTS;U0Dkq+Q0I_M3h!91*L$hrB8 zU?IPTZ*Yafc=U7RLfKdG3#;t8`ruDK`9;1U5+u^SlR6Wg^}T(!8MIp7o;o9ni3;G; z2EGO?760dsh3t4&kQA5SxFKb{_iizSeo+1Qwt!ujIv5!rmQ%p~EKPYOn29ZpsX}>F zNwy%>qyp6A5}m=9_Yh=ksZoGqL5bJfsDbgfJFkJ0iF6i{(^~HiAY)zRq&v#%xA6Dt zc(dDEZppN-MH_+>&IWIwfg_b59^`*a+h~ZDYqMS|DkkkvKlB>xn6DgSu7o+|<9b~c zA<8u+#R8|)^f9r6#Vi{UR+HQqipr4W!~%#0JRKoDtypz(DGy6xU;hrduw7V5zf=h> zB-8#p5Ebkmvbj|RHs!*@kjs^PQkfu|1sN5L4gYmn zYDEN|nwR!P(I`mO@uiiV7J2-P?=%f~S_$QQ|(fw+6-Cz@cI%EY9HVyFZ_T|3o7?Y8L0 zgFC$O>3_*TyM@#;z`)d)lElU0bk-kVCivPdxMM?V&Wg^YT{h#1OX4F4(c7(KEUoX) zorT^)LaV`RL4sjJkA_CT<#4xp^v5eRwm^fona$ke?>IT@~6&o-3h6>8#z zIBdMId)lO-M)%#MJO~8r8P178{;UE{DQ~1cs+x{$#?|PsBqHWV7acg>A_~BLh|H61 za7pk5FYx~qml7~bwENyco5e)77GV@YLvtqEy_<|pt|l8VOE~U&wU*i)jSk=k-4oKU zobIuiuv3gZ~eN|J9y@V>zQSzQ_pL?yZ16)KDqn{Czle@@)KJQmmQjWAYQ$& zvv-gFOf}D)y*s77ueQDP;^~HwlhqUA8fD-6?43slq?X~f<4-@T0;eBG4<~Ww%#!wH zaM~1hwV_w+8t_U7jhGq|w`t7e+bl_rY+B3?J5Vyvrtap~(wj{>ky$tO9-ok9Pk5DzLOTebkzzVS{ zaCN2(vw^ay@NuM)f=Y>D27stB8rt%mHgQcdlEIi~`;^pRvto4s_J`TFbvA+N{{@RN BtNs80 literal 0 HcmV?d00001 diff --git a/src/Interfaces/IMainWindow.cs b/src/Interfaces/IMainWindow.cs new file mode 100644 index 0000000..2f8b2a8 --- /dev/null +++ b/src/Interfaces/IMainWindow.cs @@ -0,0 +1,9 @@ +using AvaloniaCoreRTDemo.Interfaces; + +namespace AvaloniaCoreRTDemo +{ + public interface IMainWindow + { + IThemeSwitch ThemeSwitch { get; } + } +} diff --git a/src/Interfaces/IThemeSwitch.cs b/src/Interfaces/IThemeSwitch.cs new file mode 100644 index 0000000..45815dc --- /dev/null +++ b/src/Interfaces/IThemeSwitch.cs @@ -0,0 +1,8 @@ +namespace AvaloniaCoreRTDemo.Interfaces +{ + public interface IThemeSwitch + { + ApplicationTheme Current { get; } + void ChangeTheme(ApplicationTheme theme); + } +} diff --git a/src/MainWindow.xaml b/src/MainWindow.xaml deleted file mode 100644 index 915b90a..0000000 --- a/src/MainWindow.xaml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - Welcome to Avalonia UI + CoreRT! - - - - - - - diff --git a/src/MainWindowViewModel.cs b/src/MainWindowViewModel.cs deleted file mode 100644 index fad3eb4..0000000 --- a/src/MainWindowViewModel.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Avalonia.Media.Imaging; -using ReactiveUI; -using System; -using System.Reactive; -using Path = System.IO.Path; - -namespace AvaloniaCoreRTDemo -{ - public class MainWindowViewModel: ReactiveObject - { - public MainWindowViewModel(MainWindow window) - { - _window = window; - - DotNetImage = new Bitmap(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "dotnet.png")); - AvaloniaImage = new Bitmap(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "avalonia.png")); - - FileExitCommand = ReactiveCommand.Create(RunFileExit); - } - - public IBitmap DotNetImage - { - get { return dotNetImage; } - set { this.RaiseAndSetIfChanged(ref this.dotNetImage, value); } - } - - public IBitmap AvaloniaImage - { - get { return avaloniaImage; } - set { this.RaiseAndSetIfChanged(ref this.avaloniaImage, value); } - } - - public ReactiveCommand FileExitCommand { get; } - public void HelpAboutMethod() => RunHelpAbout(); - - void RunFileExit() - { - Environment.Exit(0); - } - - void RunHelpAbout() - { - new AboutWindow().ShowDialog(_window); - } - - private IBitmap dotNetImage; - private IBitmap avaloniaImage; - private readonly MainWindow _window; - } -} \ No newline at end of file diff --git a/src/Program.cs b/src/Program.cs index a90b6af..0bde694 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -1,4 +1,5 @@ -using Avalonia; + +using Avalonia; namespace AvaloniaCoreRTDemo { @@ -7,13 +8,14 @@ namespace AvaloniaCoreRTDemo // 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. - public static void Main(string[] args) => BuildAvaloniaApp() - .StartWithClassicDesktopLifetime(args); + 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() - .LogToTrace(); + => AppBuilder.Configure().UsePlatformDetect() +#if OSX + .UseAvaloniaNative() +#endif + .LogToTrace(); } } diff --git a/src/Windows/AboutWindow.axaml b/src/Windows/AboutWindow.axaml new file mode 100644 index 0000000..8ef32d8 --- /dev/null +++ b/src/Windows/AboutWindow.axaml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Windows/AboutWindow.axaml.cs b/src/Windows/AboutWindow.axaml.cs new file mode 100644 index 0000000..4b9ca92 --- /dev/null +++ b/src/Windows/AboutWindow.axaml.cs @@ -0,0 +1,28 @@ +using System; + +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +using AvaloniaCoreRTDemo.Windows.ViewModels; + +namespace AvaloniaCoreRTDemo.Windows +{ + public sealed partial class AboutWindow : Window + { + public AboutWindow() + { + this.InitializeComponent(); + } + + public AboutWindow(Boolean darkTheme) + { + this.InitializeComponent(darkTheme); + } + + private void InitializeComponent(Boolean darkTheme = default) + { + AvaloniaXamlLoader.Load(this); + this.DataContext = new AboutViewModel(darkTheme); + } + } +} diff --git a/src/Windows/MainWindow.axaml b/src/Windows/MainWindow.axaml new file mode 100644 index 0000000..f912199 --- /dev/null +++ b/src/Windows/MainWindow.axaml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/Windows/MainWindow.axaml.cs b/src/Windows/MainWindow.axaml.cs new file mode 100644 index 0000000..a846014 --- /dev/null +++ b/src/Windows/MainWindow.axaml.cs @@ -0,0 +1,28 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +using AvaloniaCoreRTDemo.Interfaces; +using AvaloniaCoreRTDemo.Windows.ViewModels; + +namespace AvaloniaCoreRTDemo.Windows +{ + public sealed partial class MainWindow : Window, IMainWindow + { + public MainWindow() + { + InitializeComponent(); +#if DEBUG + this.AttachDevTools(); +#endif + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + this.DataContext = new MainViewModel(this); + } + + IThemeSwitch IMainWindow.ThemeSwitch => App.Current as IThemeSwitch; + } +} diff --git a/src/Windows/MainWindowMacOS.axaml b/src/Windows/MainWindowMacOS.axaml new file mode 100644 index 0000000..4ea0793 --- /dev/null +++ b/src/Windows/MainWindowMacOS.axaml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Windows/MainWindowMacOS.axaml.cs b/src/Windows/MainWindowMacOS.axaml.cs new file mode 100644 index 0000000..9c25bcc --- /dev/null +++ b/src/Windows/MainWindowMacOS.axaml.cs @@ -0,0 +1,28 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +using AvaloniaCoreRTDemo.Interfaces; +using AvaloniaCoreRTDemo.Windows.ViewModels; + +namespace AvaloniaCoreRTDemo.Windows +{ + public partial class MainWindowMacOS : Window, IMainWindow + { + public MainWindowMacOS() + { + InitializeComponent(); +#if DEBUG + this.AttachDevTools(); +#endif + } + + private void InitializeComponent() + { + AvaloniaXamlLoader.Load(this); + this.DataContext = new MainViewModel(this); + } + + IThemeSwitch IMainWindow.ThemeSwitch => App.Current as IThemeSwitch; + } +} diff --git a/src/Windows/ViewModels/AboutViewModel.cs b/src/Windows/ViewModels/AboutViewModel.cs new file mode 100644 index 0000000..ee4b0a9 --- /dev/null +++ b/src/Windows/ViewModels/AboutViewModel.cs @@ -0,0 +1,68 @@ +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; + +using Avalonia.Media.Imaging; + +using ReactiveUI; + +namespace AvaloniaCoreRTDemo.Windows.ViewModels +{ + internal sealed class AboutViewModel : ReactiveObject + { + private readonly IBitmap _computerImage; + private readonly Boolean _darkTheme; + + public IBitmap ComputerImage => _computerImage; + public static String NCores => Environment.ProcessorCount.ToString(); + public static String OS => RuntimeInformation.OSDescription; + public static String OSArch => RuntimeInformation.OSArchitecture.ToString(); + public static String OSVersion => Environment.OSVersion.ToString(); + public static String ComputerName => Environment.MachineName; + public static String UserName => Environment.UserName; + public static String SystemPath => Environment.SystemDirectory; + public static String CurrentPath => Environment.CurrentDirectory; + public static String ProcessArch => RuntimeInformation.ProcessArchitecture.ToString(); + public static String RuntimeName => RuntimeInformation.FrameworkDescription; + public static String RuntimePath => RuntimeEnvironment.GetRuntimeDirectory(); + public static String RuntimeVersion => RuntimeEnvironment.GetSystemVersion(); + public static String FrameworkVersion => Environment.Version.ToString(); + + private String ComputerImageName + { + get + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + return !_darkTheme ? "windows.png" : "windows_d.png"; + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + return !_darkTheme ? "macos.png" : "macos_d.png"; + else + return !_darkTheme ? "linux.png" : "linux_d.png"; + } + } + + public AboutViewModel(Boolean darkTheme) + { + this._darkTheme = darkTheme; + this._computerImage = GetImageFromResources(this.ComputerImageName); + } + + ~AboutViewModel() + { + this._computerImage.Dispose(); + } + + private static Bitmap GetImageFromResources(String fileName) + { + Assembly asm = Assembly.GetExecutingAssembly(); + String resourceName = asm.GetManifestResourceNames().FirstOrDefault(str => str.EndsWith(fileName)); + if (resourceName != null) + using (Stream bitmapStream = asm.GetManifestResourceStream(resourceName)) + return new Bitmap(bitmapStream); + else + return default; + } + } +} diff --git a/src/Windows/ViewModels/MainViewModel.cs b/src/Windows/ViewModels/MainViewModel.cs new file mode 100644 index 0000000..1a72964 --- /dev/null +++ b/src/Windows/ViewModels/MainViewModel.cs @@ -0,0 +1,72 @@ +using System; +using System.Reactive; + +using Avalonia.Controls; + +using ReactiveUI; + +namespace AvaloniaCoreRTDemo.Windows.ViewModels +{ + internal sealed class MainViewModel : MainViewModelBase + where TWindow : Window, IMainWindow + { + private readonly TWindow _window; + + private Boolean _defaultLightEnable = true; + private Boolean _defaultDarkEnable = true; + private Boolean _fluentLightEnable = true; + private Boolean _fluentDarkEnable = true; + + public Boolean DefaultLightEnabled + { + get => this._defaultLightEnable; + set => this.RaiseAndSetIfChanged(ref this._defaultLightEnable, value); + } + + public Boolean DefaultDarkEnabled + { + get => this._defaultDarkEnable; + set => this.RaiseAndSetIfChanged(ref this._defaultDarkEnable, value); + } + + public Boolean FluentLightEnabled + { + get => this._fluentLightEnable; + set => this.RaiseAndSetIfChanged(ref this._fluentLightEnable, value); + } + + public Boolean FluentDarkEnabled + { + get => this._fluentDarkEnable; + set => this.RaiseAndSetIfChanged(ref this._fluentDarkEnable, value); + } + + public ReactiveCommand FileExitCommand { get; } + + public MainViewModel(TWindow window) + : base(window.ThemeSwitch) + { + this._window = window; + this.FileExitCommand = ReactiveCommand.Create(RunFileExit); + this.ChangeTheme(window.ThemeSwitch.Current); + } + + public void DefaultLightMethod() => this.ChangeTheme(ApplicationTheme.DefaultLight); + public void DefaultDarkMethod() => this.ChangeTheme(ApplicationTheme.DefaultDark); + public void FluentLightMethod() => this.ChangeTheme(ApplicationTheme.FluentLight); + public void FluentDarkMethod() => this.ChangeTheme(ApplicationTheme.FluentDark); + public void HelpAboutMethod() => base.RunHelpAbout(this._window); + + private void RunFileExit() + => Environment.Exit(0); + + private void ChangeTheme(ApplicationTheme theme) + { + this.DefaultLightEnabled = theme != ApplicationTheme.DefaultLight && theme != ApplicationTheme.FluentLight; + this.DefaultDarkEnabled = theme != ApplicationTheme.DefaultDark && theme != ApplicationTheme.FluentDark; + this.FluentLightEnabled = theme != ApplicationTheme.FluentLight; + this.FluentDarkEnabled = theme != ApplicationTheme.FluentDark; + this._window.ThemeSwitch?.ChangeTheme(theme); + } + } +} diff --git a/src/Windows/ViewModels/MainViewModelBase.cs b/src/Windows/ViewModels/MainViewModelBase.cs new file mode 100644 index 0000000..9c9f2c6 --- /dev/null +++ b/src/Windows/ViewModels/MainViewModelBase.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +using Avalonia.Controls; + +using AvaloniaCoreRTDemo.Interfaces; + +using ReactiveUI; + +namespace AvaloniaCoreRTDemo.Windows.ViewModels +{ + internal abstract class MainViewModelBase : ReactiveObject + { + private readonly IThemeSwitch _themeSwitch; + private Boolean _aboutEnable = true; + + public Boolean AboutEnabled + { + get => this._aboutEnable; + set => this.RaiseAndSetIfChanged(ref this._aboutEnable, value); + } + + public MainViewModelBase(IThemeSwitch window) + => this._themeSwitch = window; + + protected async void RunHelpAbout(Window currentWindow) + { + if (this.AboutEnabled) + try + { + this.AboutEnabled = false; + await new AboutWindow(IsDarkTheme(this._themeSwitch.Current)).ShowDialog(currentWindow); + } + finally + { + this.AboutEnabled = true; + } + } + + private static Boolean IsDarkTheme(ApplicationTheme? theme) + => theme switch + { + ApplicationTheme.DefaultDark => true, + ApplicationTheme.FluentDark => true, + _ => false, + }; + } +} diff --git a/src/nuget.config b/src/nuget.config index 8afae99..1dfbeae 100644 --- a/src/nuget.config +++ b/src/nuget.config @@ -1,11 +1,7 @@ - - - - - + diff --git a/src/rd.xml b/src/rd.xml index fa7ccfe..8e5aae8 100644 --- a/src/rd.xml +++ b/src/rd.xml @@ -9,6 +9,9 @@ + + + - + + MainWindowMacOS.axaml From 3073afd6c7f4f08cf3f4a5f02dd2ee3ab4f1763e Mon Sep 17 00:00:00 2001 From: Joseph Moreno <44370115+josephmoresena@users.noreply.github.com> Date: Sun, 12 Dec 2021 00:24:32 -0500 Subject: [PATCH 3/8] Artifacts information --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 4a6491d..12aff36 100644 --- a/README.md +++ b/README.md @@ -37,3 +37,9 @@ Feel free to use this sample as a base for your projects. When developing, keep in mind that Avalonia uses Reflection extensively. NativeAOT, being an AOT compiler, needs your help to get reflection right. Refer to `rd.xml` file in the solution. In it, you describe all assemblies and types which your app would potentially reflect over. For more information, see [Reflection in AOT mode](https://github.com/dotnet/runtimelab/blob/feature/NativeAOT/docs/using-nativeaot/reflection-in-aot-mode.md) This project is configured to help you debug issues with publishing. Before publishing, check the CSPROJ file and modify the various Ilc* properties according to [Optimizing programs targeting Native AOT](https://github.com/dotnet/runtimelab/blob/feature/NativeAOT/docs/using-nativeaot/optimizing.md) document. + +## Artifact test + +For any change in this repo we will build the artifact for Windows-x64, Linux-x64 and macOS-x64. +You can download them from workflows run results. +For run the artifact on macOS make sure to allow the application inSystem Preferences, Security & Privacy, General. From eab730c1f2f63be033be935851c7ea82021f3812 Mon Sep 17 00:00:00 2001 From: Joseph Moreno <44370115+josephmoresena@users.noreply.github.com> Date: Mon, 13 Dec 2021 16:17:55 -0500 Subject: [PATCH 4/8] Sample improves * No remove axaml files for makes the sample simpler. * Creation of utilities class. * Avoid using ref Bitmap on MainViewModel. * Avoid application crashing when dotnet.png or avalonia.png are missing. Using an replace embeded image instead. * Conditionally invoke of UseAvaloniaNative at runtime. --- src/App.axaml.cs | 8 +--- src/AvaloniaCoreRTDemo.csproj | 4 -- src/Controls/ViewModels/MainViewModel.cs | 26 ++++++------- src/Images/broken-link.png | Bin 0 -> 15464 bytes src/Program.cs | 12 +++--- src/Utilities.cs | 39 ++++++++++++++++++++ src/Windows/ViewModels/AboutViewModel.cs | 26 +++---------- src/Windows/ViewModels/MainViewModel.cs | 2 +- src/Windows/ViewModels/MainViewModelBase.cs | 6 +-- 9 files changed, 69 insertions(+), 54 deletions(-) create mode 100644 src/Images/broken-link.png create mode 100644 src/Utilities.cs diff --git a/src/App.axaml.cs b/src/App.axaml.cs index 42529d0..e05cb41 100644 --- a/src/App.axaml.cs +++ b/src/App.axaml.cs @@ -1,3 +1,4 @@ + using Avalonia; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Markup.Xaml; @@ -30,12 +31,7 @@ namespace AvaloniaCoreRTDemo this.InitializeThemes(); if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - desktop.MainWindow = -#if !OSX - new MainWindow(); -#else - new MainWindowMacOS(); -#endif + desktop.MainWindow = !Utilities.IsOSX ? new MainWindow() : new MainWindowMacOS(); this.DataContext = desktop.MainWindow.DataContext; } base.OnFrameworkInitializationCompleted(); diff --git a/src/AvaloniaCoreRTDemo.csproj b/src/AvaloniaCoreRTDemo.csproj index 73edcee..7e20a93 100644 --- a/src/AvaloniaCoreRTDemo.csproj +++ b/src/AvaloniaCoreRTDemo.csproj @@ -99,15 +99,11 @@ MainWindow.axaml - - MainWindowMacOS.axaml - - \ No newline at end of file diff --git a/src/Controls/ViewModels/MainViewModel.cs b/src/Controls/ViewModels/MainViewModel.cs index ee69584..29fc9f4 100644 --- a/src/Controls/ViewModels/MainViewModel.cs +++ b/src/Controls/ViewModels/MainViewModel.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Runtime.CompilerServices; using Avalonia.Media.Imaging; @@ -13,22 +12,23 @@ namespace AvaloniaCoreRTDemo.Controls.ViewModels private readonly IBitmap _dotNetImage; private readonly IBitmap _avaloniaImage; - public IBitmap DotNetImage - { - get { return _dotNetImage; } - set { this.RaiseAndSetIfChanged(ref Unsafe.AsRef(this._dotNetImage), value); } - } + public IBitmap DotNetImage => this._dotNetImage; - public IBitmap AvaloniaImage - { - get { return _avaloniaImage; } - set { this.RaiseAndSetIfChanged(ref Unsafe.AsRef(this._avaloniaImage), value); } - } + public IBitmap AvaloniaImage => this._avaloniaImage; public MainViewModel() { - this._dotNetImage = new Bitmap(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "dotnet.png")); - this._avaloniaImage = new Bitmap(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "avalonia.png")); + this._dotNetImage = Utilities.GetImageFromFile(GetImageFullPath("dotnet.png")); + this._avaloniaImage = Utilities.GetImageFromFile(GetImageFullPath("avalonia.png")); + } + + private static String GetImageFullPath(String fileName) + => Path.Combine(AppDomain.CurrentDomain.BaseDirectory, fileName); + + ~MainViewModel() + { + this._dotNetImage.Dispose(); + this._avaloniaImage.Dispose(); } } } diff --git a/src/Images/broken-link.png b/src/Images/broken-link.png new file mode 100644 index 0000000000000000000000000000000000000000..585ee1c69da73bde6f11a4b633229295b562fe7e GIT binary patch literal 15464 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 eT35vr1^ypBP2?iy5Uwr&0000 AppBuilder.Configure().UsePlatformDetect() -#if OSX - .UseAvaloniaNative() -#endif + .UseAvaloniaNativeOSX() .LogToTrace(); + + private static AppBuilder UseAvaloniaNativeOSX(this AppBuilder appBuilder) + => Utilities.IsOSX ? appBuilder.UseAvaloniaNative() : appBuilder; } } diff --git a/src/Utilities.cs b/src/Utilities.cs new file mode 100644 index 0000000..4296e91 --- /dev/null +++ b/src/Utilities.cs @@ -0,0 +1,39 @@ +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; + +using Avalonia.Media.Imaging; + +namespace AvaloniaCoreRTDemo +{ + internal static class Utilities + { + public static readonly Boolean IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + public static readonly Boolean IsOSX = RuntimeInformation.IsOSPlatform(OSPlatform.OSX); + + public static Bitmap GetImageFromResources(String fileName) + { + Assembly asm = Assembly.GetExecutingAssembly(); + String resourceName = asm.GetManifestResourceNames().FirstOrDefault(str => str.EndsWith(fileName)); + if (resourceName != null) + using (Stream bitmapStream = asm.GetManifestResourceStream(resourceName)) + return new Bitmap(bitmapStream); + else + return default; + } + + public static Bitmap GetImageFromFile(String path) + { + try + { + return new Bitmap(path); + } + catch (Exception) + { + return GetImageFromResources("broken-link.png"); + } + } + } +} diff --git a/src/Windows/ViewModels/AboutViewModel.cs b/src/Windows/ViewModels/AboutViewModel.cs index ee4b0a9..b09161e 100644 --- a/src/Windows/ViewModels/AboutViewModel.cs +++ b/src/Windows/ViewModels/AboutViewModel.cs @@ -1,7 +1,4 @@ using System; -using System.IO; -using System.Linq; -using System.Reflection; using System.Runtime.InteropServices; using Avalonia.Media.Imaging; @@ -34,35 +31,24 @@ namespace AvaloniaCoreRTDemo.Windows.ViewModels { get { - if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - return !_darkTheme ? "windows.png" : "windows_d.png"; - else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - return !_darkTheme ? "macos.png" : "macos_d.png"; + if (Utilities.IsWindows) + return !this._darkTheme ? "windows.png" : "windows_d.png"; + else if (Utilities.IsOSX) + return !this._darkTheme ? "macos.png" : "macos_d.png"; else - return !_darkTheme ? "linux.png" : "linux_d.png"; + return !this._darkTheme ? "linux.png" : "linux_d.png"; } } public AboutViewModel(Boolean darkTheme) { this._darkTheme = darkTheme; - this._computerImage = GetImageFromResources(this.ComputerImageName); + this._computerImage = Utilities.GetImageFromResources(this.ComputerImageName); } ~AboutViewModel() { this._computerImage.Dispose(); } - - private static Bitmap GetImageFromResources(String fileName) - { - Assembly asm = Assembly.GetExecutingAssembly(); - String resourceName = asm.GetManifestResourceNames().FirstOrDefault(str => str.EndsWith(fileName)); - if (resourceName != null) - using (Stream bitmapStream = asm.GetManifestResourceStream(resourceName)) - return new Bitmap(bitmapStream); - else - return default; - } } } diff --git a/src/Windows/ViewModels/MainViewModel.cs b/src/Windows/ViewModels/MainViewModel.cs index 1a72964..a0acc74 100644 --- a/src/Windows/ViewModels/MainViewModel.cs +++ b/src/Windows/ViewModels/MainViewModel.cs @@ -55,7 +55,7 @@ namespace AvaloniaCoreRTDemo.Windows.ViewModels public void DefaultDarkMethod() => this.ChangeTheme(ApplicationTheme.DefaultDark); public void FluentLightMethod() => this.ChangeTheme(ApplicationTheme.FluentLight); public void FluentDarkMethod() => this.ChangeTheme(ApplicationTheme.FluentDark); - public void HelpAboutMethod() => base.RunHelpAbout(this._window); + public override void HelpAboutMethod() => base.RunHelpAbout(this._window); private void RunFileExit() => Environment.Exit(0); diff --git a/src/Windows/ViewModels/MainViewModelBase.cs b/src/Windows/ViewModels/MainViewModelBase.cs index 9c9f2c6..c7faffd 100644 --- a/src/Windows/ViewModels/MainViewModelBase.cs +++ b/src/Windows/ViewModels/MainViewModelBase.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Avalonia.Controls; @@ -26,6 +22,8 @@ namespace AvaloniaCoreRTDemo.Windows.ViewModels public MainViewModelBase(IThemeSwitch window) => this._themeSwitch = window; + public abstract void HelpAboutMethod(); + protected async void RunHelpAbout(Window currentWindow) { if (this.AboutEnabled) From 6789fbaec7021a682d6a179c24283172d330bc29 Mon Sep 17 00:00:00 2001 From: Joseph Moreno <44370115+josephmoresena@users.noreply.github.com> Date: Wed, 22 Dec 2021 17:08:44 -0500 Subject: [PATCH 5/8] Avalonia UI 0.10.10 -> 0.10.11 --- src/AvaloniaCoreRTDemo.csproj | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/AvaloniaCoreRTDemo.csproj b/src/AvaloniaCoreRTDemo.csproj index 7e20a93..38802a6 100644 --- a/src/AvaloniaCoreRTDemo.csproj +++ b/src/AvaloniaCoreRTDemo.csproj @@ -43,11 +43,11 @@ - + - - - + + + @@ -106,4 +106,4 @@ MainWindowMacOS.axaml - \ No newline at end of file + From 8e38c08db3aad838d9f7d6643b752723a56d8a18 Mon Sep 17 00:00:00 2001 From: Joseph Moreno <44370115+josephmoresena@users.noreply.github.com> Date: Sun, 13 Feb 2022 18:35:14 -0500 Subject: [PATCH 6/8] Avalonia 0.10.12 --- src/AvaloniaCoreRTDemo.csproj | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/AvaloniaCoreRTDemo.csproj b/src/AvaloniaCoreRTDemo.csproj index 38802a6..837215a 100644 --- a/src/AvaloniaCoreRTDemo.csproj +++ b/src/AvaloniaCoreRTDemo.csproj @@ -43,13 +43,13 @@ - + - - - + + + - + From bfec7e3ad2f0c3bdb404ab9f1a835693fcef8d13 Mon Sep 17 00:00:00 2001 From: Joseph Moreno <44370115+josephmoresena@users.noreply.github.com> Date: Tue, 15 Feb 2022 14:13:06 -0500 Subject: [PATCH 7/8] Update nuget.config --- src/nuget.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/nuget.config b/src/nuget.config index 1dfbeae..73b40f7 100644 --- a/src/nuget.config +++ b/src/nuget.config @@ -1,7 +1,7 @@ - + From 4aacf93202903ac38173ddf6b06c7a03ab98dadd Mon Sep 17 00:00:00 2001 From: Joseph Moreno <44370115+josephmoresena@users.noreply.github.com> Date: Tue, 15 Feb 2022 14:32:10 -0500 Subject: [PATCH 8/8] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 12aff36..c0ed692 100644 --- a/README.md +++ b/README.md @@ -42,4 +42,4 @@ This project is configured to help you debug issues with publishing. Before publ For any change in this repo we will build the artifact for Windows-x64, Linux-x64 and macOS-x64. You can download them from workflows run results. -For run the artifact on macOS make sure to allow the application inSystem Preferences, Security & Privacy, General. +For run the artifact on macOS make sure to allow the application in System Preferences, Security & Privacy, General.