Răsfoiți Sursa

Merge pull request #9413 from AvaloniaUI/assetinclude-intrinsic

Resolve StyleInclude and ResourceInclude at compile time + revisit StyleInclude usage from the codebehind
Jumar Macato 3 ani în urmă
părinte
comite
ed2a4a3d00
48 a modificat fișierele cu 820 adăugiri și 766 ștergeri
  1. 1 0
      packages/Avalonia/AvaloniaBuildTasks.targets
  2. 9 0
      samples/ControlCatalog/App.xaml
  3. 96 62
      samples/ControlCatalog/App.xaml.cs
  4. 2 37
      samples/ControlCatalog/MainView.xaml.cs
  5. 20 3
      src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs
  6. 5 3
      src/Avalonia.Build.Tasks/Program.cs
  7. 234 36
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
  8. 3 6
      src/Avalonia.Themes.Fluent/Accents/AccentColors.xaml
  9. 5 7
      src/Avalonia.Themes.Fluent/Accents/Base.xaml
  10. 4 7
      src/Avalonia.Themes.Fluent/Accents/BaseDark.xaml
  11. 3 6
      src/Avalonia.Themes.Fluent/Accents/BaseLight.xaml
  12. 4 7
      src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml
  13. 3 6
      src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml
  14. 1 0
      src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj
  15. 0 1
      src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml
  16. 0 12
      src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml.cs
  17. 1 2
      src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml
  18. 15 17
      src/Avalonia.Themes.Fluent/DensityStyles/Compact.xaml
  19. 0 9
      src/Avalonia.Themes.Fluent/FluentDark.xaml
  20. 0 9
      src/Avalonia.Themes.Fluent/FluentLight.xaml
  21. 0 244
      src/Avalonia.Themes.Fluent/FluentTheme.cs
  22. 21 0
      src/Avalonia.Themes.Fluent/FluentTheme.xaml
  23. 124 0
      src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs
  24. 2 6
      src/Avalonia.Themes.Fluent/IBitmapToImageConverter.cs
  25. 0 20
      src/Avalonia.Themes.Fluent/InverseBooleanValueConverter.cs
  26. 3 5
      src/Avalonia.Themes.Simple/Accents/Base.xaml
  27. 3 6
      src/Avalonia.Themes.Simple/Accents/BaseDark.xaml
  28. 3 6
      src/Avalonia.Themes.Simple/Accents/BaseLight.xaml
  29. 1 0
      src/Avalonia.Themes.Simple/Avalonia.Themes.Simple.csproj
  30. 1 3
      src/Avalonia.Themes.Simple/Controls/NativeMenuBar.xaml
  31. 2 2
      src/Avalonia.Themes.Simple/IBitmapToImageConverter.cs
  32. 0 20
      src/Avalonia.Themes.Simple/InverseBooleanValueConverter.cs
  33. 0 166
      src/Avalonia.Themes.Simple/SimpleTheme.cs
  34. 17 0
      src/Avalonia.Themes.Simple/SimpleTheme.xaml
  35. 69 0
      src/Avalonia.Themes.Simple/SimpleTheme.xaml.cs
  36. 2 1
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs
  37. 93 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlAssetIncludeTransformer.cs
  38. 6 0
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  39. 1 2
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  40. 0 22
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StyleIncludeExtension.cs
  41. 20 10
      src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs
  42. 2 2
      src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs
  43. 13 0
      src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs
  44. 1 1
      tests/Avalonia.Benchmarks/Themes/FluentBenchmark.cs
  45. 2 2
      tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs
  46. 4 4
      tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj
  47. 1 5
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
  48. 23 9
      tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs

+ 1 - 0
packages/Avalonia/AvaloniaBuildTasks.targets

@@ -99,6 +99,7 @@
       AssemblyFile="@(IntermediateAssembly)"
       ReferencesFilePath="$(AvaloniaXamlReferencesTemporaryFilePath)"
       OriginalCopyPath="$(AvaloniaXamlOriginalCopyFilePath)"
+      RefAssemblyFile="@(IntermediateRefAssembly)"
       ProjectDirectory="$(MSBuildProjectDirectory)"
       VerifyIl="$(AvaloniaXamlIlVerifyIl)"
       ReportImportance="$(AvaloniaXamlReportImportance)"

+ 9 - 0
samples/ControlCatalog/App.xaml

@@ -9,6 +9,15 @@
       <ResourceDictionary.MergedDictionaries>
         <ResourceInclude Source="avares://ControlSamples/HamburgerMenu/HamburgerMenu.xaml" />
       </ResourceDictionary.MergedDictionaries>
+
+      <StyleInclude x:Key="DataGridFluent" Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml" />
+      <StyleInclude x:Key="DataGridSimple" Source="avares://Avalonia.Controls.DataGrid/Themes/Simple.xaml" />
+      <StyleInclude x:Key="ColorPickerFluent" Source="avares://Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml" />
+      <StyleInclude x:Key="ColorPickerSimple" Source="avares://Avalonia.Controls.ColorPicker/Themes/Simple/Simple.xaml" />
+      <ResourceInclude x:Key="FluentAccentColors" Source="avares://Avalonia.Themes.Fluent/Accents/AccentColors.xaml" />
+      <ResourceInclude x:Key="FluentBaseLightColors" Source="avares://Avalonia.Themes.Fluent/Accents/BaseLight.xaml" />
+      <ResourceInclude x:Key="FluentBaseDarkColors" Source="avares://Avalonia.Themes.Fluent/Accents/BaseDark.xaml" />
+      <ResourceInclude x:Key="FluentBaseColors" Source="avares://Avalonia.Themes.Fluent/Accents/Base.xaml" />
     </ResourceDictionary>
   </Application.Resources>
   <Application.Styles>

+ 96 - 62
samples/ControlCatalog/App.xaml.cs

@@ -3,85 +3,46 @@ using Avalonia;
 using Avalonia.Controls;
 using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Markup.Xaml;
-using Avalonia.Markup.Xaml.Styling;
 using Avalonia.Styling;
 using Avalonia.Themes.Simple;
 using Avalonia.Themes.Fluent;
+using ControlCatalog.Models;
 using ControlCatalog.ViewModels;
 
 namespace ControlCatalog
 {
     public class App : Application
     {
+        private readonly Styles _themeStylesContainer = new();
+        private FluentTheme? _fluentTheme;
+        private SimpleTheme? _simpleTheme;
+        private IResourceDictionary? _fluentBaseLightColors, _fluentBaseDarkColors;
+        private IStyle? _colorPickerFluent, _colorPickerSimple;
+        private IStyle? _dataGridFluent, _dataGridSimple;
+        
         public App()
         {
             DataContext = new ApplicationViewModel();
         }
 
-        public static readonly StyleInclude ColorPickerFluent = new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
-        {
-            Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml")
-        };
-
-        public static readonly StyleInclude ColorPickerSimple = new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
-        {
-            Source = new Uri("avares://Avalonia.Controls.ColorPicker/Themes/Simple/Simple.xaml")
-        };
-
-        public static readonly StyleInclude DataGridFluent = new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
-        {
-            Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml")
-        };
-
-        public static readonly StyleInclude DataGridSimple = new StyleInclude(new Uri("avares://ControlCatalog/Styles"))
-        {
-            Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Simple.xaml")
-        };
-
-        public static FluentTheme Fluent = new FluentTheme(new Uri("avares://ControlCatalog/Styles"));
-
-        public static SimpleTheme Simple = new SimpleTheme(new Uri("avares://ControlCatalog/Styles"));
-
-        public static Styles SimpleLight = new Styles
-        {
-            new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
-            {
-                Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/AccentColors.xaml")
-            },
-            new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
-            {
-                Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml")
-            },
-            new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
-            {
-                Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseLight.xaml")
-            },
-            Simple
-        };
-
-        public static Styles SimpleDark = new Styles
-        {
-            new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
-            {
-                Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/AccentColors.xaml")
-            },
-            new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
-            {
-                Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml")
-            },
-            new StyleInclude(new Uri("resm:Styles?assembly=ControlCatalog"))
-            {
-                Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseDark.xaml")
-            },
-            Simple
-        };
-
         public override void Initialize()
         {
-            Styles.Insert(0, Fluent);
-            Styles.Insert(1, ColorPickerFluent);
-            Styles.Insert(2, DataGridFluent);
+            Styles.Add(_themeStylesContainer);
+
             AvaloniaXamlLoader.Load(this);
+
+            _fluentTheme = new FluentTheme();
+            _simpleTheme = new SimpleTheme();
+            _simpleTheme.Resources.MergedDictionaries.Add((IResourceDictionary)Resources["FluentAccentColors"]!);
+            _simpleTheme.Resources.MergedDictionaries.Add((IResourceDictionary)Resources["FluentBaseColors"]!);
+            _colorPickerFluent = (IStyle)Resources["ColorPickerFluent"]!;
+            _colorPickerSimple = (IStyle)Resources["ColorPickerSimple"]!;
+            _dataGridFluent = (IStyle)Resources["DataGridFluent"]!;
+            _dataGridSimple = (IStyle)Resources["DataGridSimple"]!;
+            _fluentBaseLightColors = (IResourceDictionary)Resources["FluentBaseLightColors"]!;
+            _fluentBaseDarkColors = (IResourceDictionary)Resources["FluentBaseDarkColors"]!;
+            
+            SetThemeVariant(CatalogTheme.FluentLight);
         }
 
         public override void OnFrameworkInitializationCompleted()
@@ -97,5 +58,78 @@ namespace ControlCatalog
 
             base.OnFrameworkInitializationCompleted();
         }
+
+        private CatalogTheme _prevTheme;
+        public static CatalogTheme CurrentTheme => ((App)Current!)._prevTheme; 
+        public static void SetThemeVariant(CatalogTheme theme)
+        {
+            var app = (App)Current!;
+            var prevTheme = app._prevTheme;
+            app._prevTheme = theme;
+            var shouldReopenWindow = theme switch
+            {
+                CatalogTheme.FluentLight => prevTheme is CatalogTheme.SimpleDark or CatalogTheme.SimpleLight,
+                CatalogTheme.FluentDark => prevTheme is CatalogTheme.SimpleDark or CatalogTheme.SimpleLight,
+                CatalogTheme.SimpleLight => prevTheme is CatalogTheme.FluentDark or CatalogTheme.FluentLight,
+                CatalogTheme.SimpleDark => prevTheme is CatalogTheme.FluentDark or CatalogTheme.FluentLight,
+                _ => throw new ArgumentOutOfRangeException(nameof(theme), theme, null)
+            };
+            
+            if (app._themeStylesContainer.Count == 0)
+            {
+                app._themeStylesContainer.Add(new Style());
+                app._themeStylesContainer.Add(new Style());
+                app._themeStylesContainer.Add(new Style());
+            }
+            
+            if (theme == CatalogTheme.FluentLight)
+            {
+                app._fluentTheme!.Mode = FluentThemeMode.Light;
+                app._themeStylesContainer[0] = app._fluentTheme;
+                app._themeStylesContainer[1] = app._colorPickerFluent!;
+                app._themeStylesContainer[2] = app._dataGridFluent!;
+            }
+            else if (theme == CatalogTheme.FluentDark)
+            {
+                app._fluentTheme!.Mode = FluentThemeMode.Dark;
+                app._themeStylesContainer[0] = app._fluentTheme;
+                app._themeStylesContainer[1] = app._colorPickerFluent!;
+                app._themeStylesContainer[2] = app._dataGridFluent!;
+            }
+            else if (theme == CatalogTheme.SimpleLight)
+            {
+                app._simpleTheme!.Mode = SimpleThemeMode.Light;
+                app._simpleTheme.Resources.MergedDictionaries.Remove(app._fluentBaseDarkColors!);
+                app._simpleTheme.Resources.MergedDictionaries.Add(app._fluentBaseLightColors!);
+                app._themeStylesContainer[0] = app._simpleTheme;
+                app._themeStylesContainer[1] = app._colorPickerSimple!;
+                app._themeStylesContainer[2] = app._dataGridSimple!;
+            }
+            else if (theme == CatalogTheme.SimpleDark)
+            {
+                app._simpleTheme!.Mode = SimpleThemeMode.Dark;
+                app._simpleTheme.Resources.MergedDictionaries.Remove(app._fluentBaseLightColors!);
+                app._simpleTheme.Resources.MergedDictionaries.Add(app._fluentBaseDarkColors!);
+                app._themeStylesContainer[0] = app._simpleTheme;
+                app._themeStylesContainer[1] = app._colorPickerSimple!;
+                app._themeStylesContainer[2] = app._dataGridSimple!;
+            }
+
+            if (shouldReopenWindow)
+            {
+                if (app.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktopLifetime)
+                {
+                    var oldWindow = desktopLifetime.MainWindow;
+                    var newWindow = new MainWindow();
+                    desktopLifetime.MainWindow = newWindow;
+                    newWindow.Show();
+                    oldWindow?.Close();
+                }
+                else if (app.ApplicationLifetime is ISingleViewApplicationLifetime singleViewLifetime)
+                {
+                    singleViewLifetime.MainView = new MainView();
+                }
+            }
+        }
     }
 }

+ 2 - 37
samples/ControlCatalog/MainView.xaml.cs

@@ -6,7 +6,6 @@ using Avalonia.Controls.ApplicationLifetimes;
 using Avalonia.Markup.Xaml;
 using Avalonia.Media;
 using Avalonia.Media.Immutable;
-using Avalonia.Themes.Fluent;
 using ControlCatalog.Models;
 using ControlCatalog.Pages;
 
@@ -31,46 +30,12 @@ namespace ControlCatalog
             }
 
             var themes = this.Get<ComboBox>("Themes");
+            themes.SelectedItem = App.CurrentTheme;
             themes.SelectionChanged += (sender, e) =>
             {
                 if (themes.SelectedItem is CatalogTheme theme)
                 {
-                    var themeStyle = Application.Current!.Styles[0];
-                    if (theme == CatalogTheme.FluentLight)
-                    {
-                        if (App.Fluent.Mode != FluentThemeMode.Light)
-                        {
-                            App.Fluent.Mode = FluentThemeMode.Light;
-                        }
-                        Application.Current.Styles[0] = App.Fluent;
-                        Application.Current.Styles[1] = App.ColorPickerFluent;
-                        Application.Current.Styles[2] = App.DataGridFluent;
-                    }
-                    else if (theme == CatalogTheme.FluentDark)
-                    {
-
-                        if (App.Fluent.Mode != FluentThemeMode.Dark)
-                        {
-                            App.Fluent.Mode = FluentThemeMode.Dark;
-                        }
-                        Application.Current.Styles[0] = App.Fluent;
-                        Application.Current.Styles[1] = App.ColorPickerFluent;
-                        Application.Current.Styles[2] = App.DataGridFluent;
-                    }
-                    else if (theme == CatalogTheme.SimpleLight)
-                    {
-                        App.Simple.Mode = Avalonia.Themes.Simple.SimpleThemeMode.Light;
-                        Application.Current.Styles[0] = App.SimpleLight;
-                        Application.Current.Styles[1] = App.ColorPickerSimple;
-                        Application.Current.Styles[2] = App.DataGridSimple;
-                    }
-                    else if (theme == CatalogTheme.SimpleDark)
-                    {
-                        App.Simple.Mode = Avalonia.Themes.Simple.SimpleThemeMode.Dark;
-                        Application.Current.Styles[0] = App.SimpleDark;
-                        Application.Current.Styles[1] = App.ColorPickerSimple;
-                        Application.Current.Styles[2] = App.DataGridSimple;
-                    }
+                    App.SetThemeVariant(theme);
                 }
             };
 

+ 20 - 3
src/Avalonia.Build.Tasks/CompileAvaloniaXamlTask.cs

@@ -12,12 +12,16 @@ namespace Avalonia.Build.Tasks
             Enum.TryParse(ReportImportance, true, out MessageImportance outputImportance);
 
             OutputPath = OutputPath ?? AssemblyFile;
+            RefOutputPath = RefOutputPath ?? RefAssemblyFile;
             var outputPdb = GetPdbPath(OutputPath);
             var input = AssemblyFile;
+            var refInput = RefOutputPath;
             var inputPdb = GetPdbPath(input);
-            // Make a copy and delete the original file to prevent MSBuild from thinking that everything is OK 
+            // Make a copy and delete the original file to prevent MSBuild from thinking that everything is OK
             if (OriginalCopyPath != null)
             {
+                var originalCopyPathRef = Path.ChangeExtension(OriginalCopyPath, ".ref.dll");
+                
                 File.Copy(AssemblyFile, OriginalCopyPath, true);
                 input = OriginalCopyPath;
                 File.Delete(AssemblyFile);
@@ -29,14 +33,24 @@ namespace Avalonia.Build.Tasks
                     File.Delete(inputPdb);
                     inputPdb = copyPdb;
                 }
+                
+                if (!string.IsNullOrWhiteSpace(RefAssemblyFile) && File.Exists(RefAssemblyFile))
+                {
+                    // We also copy ref assembly just for case if needed later for testing.
+                    // But do not remove the original one, as MSBuild actually complains about it with multi-thread compiling.
+                    File.Copy(RefAssemblyFile, originalCopyPathRef, true);
+                    refInput = originalCopyPathRef;
+                }
             }
 
             var msg = $"CompileAvaloniaXamlTask -> AssemblyFile:{AssemblyFile}, ProjectDirectory:{ProjectDirectory}, OutputPath:{OutputPath}";
             BuildEngine.LogMessage(msg, outputImportance < MessageImportance.Low ? MessageImportance.High : outputImportance);
 
-            var res = XamlCompilerTaskExecutor.Compile(BuildEngine, input,
+            var res = XamlCompilerTaskExecutor.Compile(BuildEngine,
+                input, OutputPath,
+                refInput, RefOutputPath,
                 File.ReadAllLines(ReferencesFilePath).Where(l => !string.IsNullOrWhiteSpace(l)).ToArray(),
-                ProjectDirectory, OutputPath, VerifyIl, DefaultCompileBindings, outputImportance,
+                ProjectDirectory, VerifyIl, DefaultCompileBindings, outputImportance,
                 (SignAssembly && !DelaySign) ? AssemblyOriginatorKeyFile : null, SkipXamlCompilation, DebuggerLaunch);
             if (!res.Success)
                 return false;
@@ -68,6 +82,9 @@ namespace Avalonia.Build.Tasks
         [Required]
         public string ProjectDirectory { get; set; }
         
+        public string RefAssemblyFile { get; set; }
+        public string RefOutputPath { get; set; }
+        
         public string OutputPath { get; set; }
 
         public bool VerifyIl { get; set; }

+ 5 - 3
src/Avalonia.Build.Tasks/Program.cs

@@ -14,7 +14,7 @@ namespace Avalonia.Build.Tasks
 
         static int Main(string[] args)
         {
-            if (args.Length != 3)
+            if (args.Length < 3)
             {
                 if (args.Length == 1)
                 {
@@ -27,11 +27,12 @@ namespace Avalonia.Build.Tasks
                     Console.WriteLine(@$"Usage:
     1) dotnet ./Avalonia.Build.Tasks.dll <ReferencesOutputPath>
        , where <ReferencesOutputPath> likes {referencesOutputPath}
-    2) dotnet ./Avalonia.Build.Tasks.dll <AssemblyFilePath> <ReferencesFilePath> <OutputPath>
+    2) dotnet ./Avalonia.Build.Tasks.dll <AssemblyFilePath> <ReferencesFilePath> <OutputPath> <RefAssemblyFile>
        , where:
            - <AssemblyFilePath> likes {referencesOutputPath}/{OriginalDll}
            - <ReferencesFilePath> likes {referencesOutputPath}/{References}
-           - <OutputPath> likes {referencesOutputPath}/{OutDll}");
+           - <OutputPath> likes {referencesOutputPath}/{OutDll}
+           - <RefAssemblyFile> Likes {referencesOutputPath}/original.ref.dll");
 
                     return 1;
                 }
@@ -42,6 +43,7 @@ namespace Avalonia.Build.Tasks
                 AssemblyFile = args[0],
                 ReferencesFilePath = args[1],
                 OutputPath = args[2],
+                RefAssemblyFile = args.Length > 3 ? args[3] : null, 
                 BuildEngine = new ConsoleBuildEngine(),
                 ProjectDirectory = Directory.GetCurrentDirectory(),
                 VerifyIl = true

+ 234 - 36
src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs

@@ -1,7 +1,7 @@
 using System;
+using System.Collections.Generic;
 using System.IO;
 using System.Linq;
-using System.Reflection;
 using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions;
 using Microsoft.Build.Framework;
 using Mono.Cecil;
@@ -21,6 +21,8 @@ namespace Avalonia.Build.Tasks
 {
     public static partial class XamlCompilerTaskExecutor
     {
+        private const string CompiledAvaloniaXamlNamespace = "CompiledAvaloniaXaml";
+        
         static bool CheckXamlName(IResource r) => r.Name.ToLowerInvariant().EndsWith(".xaml")
                                                || r.Name.ToLowerInvariant().EndsWith(".paml")
                                                || r.Name.ToLowerInvariant().EndsWith(".axaml");
@@ -37,42 +39,57 @@ namespace Avalonia.Build.Tasks
             }
         }
 
-        public static CompileResult Compile(IBuildEngine engine, string input, string[] references,
-            string projectDirectory,
-            string output, bool verifyIl, bool defaultCompileBindings, MessageImportance logImportance, string strongNameKey,
+        public static CompileResult Compile(IBuildEngine engine,
+            string input, string output,
+            string refInput, string refOutput,
+            string[] references, string projectDirectory,
+            bool verifyIl, bool defaultCompileBindings, MessageImportance logImportance, string strongNameKey,
             bool skipXamlCompilation)
         {
-            return Compile(engine, input, references, projectDirectory, output, verifyIl, defaultCompileBindings, logImportance, strongNameKey, skipXamlCompilation, debuggerLaunch:false);
+            return Compile(engine, input, output, refInput, refOutput, references, projectDirectory, verifyIl, defaultCompileBindings, logImportance, strongNameKey, skipXamlCompilation, debuggerLaunch:false);
         }
 
-        internal static CompileResult Compile(IBuildEngine engine, string input, string[] references,
-            string projectDirectory,
-            string output, bool verifyIl, bool defaultCompileBindings, MessageImportance logImportance, string strongNameKey, bool skipXamlCompilation, bool debuggerLaunch)
+        internal static CompileResult Compile(IBuildEngine engine,
+            string input, string output,
+            string refInput, string refOutput,
+            string[] references, string projectDirectory,
+            bool verifyIl, bool defaultCompileBindings, MessageImportance logImportance, string strongNameKey, bool skipXamlCompilation, bool debuggerLaunch)
         {
             try
             {
-                var typeSystem = new CecilTypeSystem(
-                    references.Where(r => !r.ToLowerInvariant().EndsWith("avalonia.build.tasks.dll")),
-                    input);
+                references = references.Where(r => !r.ToLowerInvariant().EndsWith("avalonia.build.tasks.dll")).ToArray();
+                var typeSystem = new CecilTypeSystem(references, input);
+                var refTypeSystem = !string.IsNullOrWhiteSpace(refInput) && File.Exists(refInput) ? new CecilTypeSystem(references, refInput) : null;
 
                 var asm = typeSystem.TargetAssemblyDefinition;
-
+                var refAsm = refTypeSystem?.TargetAssemblyDefinition;
                 if (!skipXamlCompilation)
                 {
-                    var compileRes = CompileCore(engine, typeSystem, projectDirectory, verifyIl, defaultCompileBindings,
-                        logImportance, debuggerLaunch);
-                    if (compileRes == null)
-                        return new CompileResult(true);
-                    if (compileRes == false)
-                        return new CompileResult(false);
+	                var compileRes = CompileCore(engine, typeSystem, projectDirectory, verifyIl, defaultCompileBindings, logImportance, debuggerLaunch);
+	                if (compileRes == null)
+	                    return new CompileResult(true);
+	                if (compileRes == false)
+	                    return new CompileResult(false);
+
+	                if (refTypeSystem is not null)
+	                {
+	                    var refCompileRes = CompileCoreForRefAssembly(engine, typeSystem, refTypeSystem);
+	                    if (refCompileRes == false)
+	                        return new CompileResult(false);
+	                }
                 }
 
                 var writerParameters = new WriterParameters { WriteSymbols = asm.MainModule.HasSymbols };
                 if (!string.IsNullOrWhiteSpace(strongNameKey))
-                    writerParameters.StrongNameKeyBlob = File.ReadAllBytes(strongNameKey);
+	                writerParameters.StrongNameKeyBlob = File.ReadAllBytes(strongNameKey);
 
                 asm.Write(output, writerParameters);
 
+                var refWriterParameters = new WriterParameters { WriteSymbols = false };
+                if (!string.IsNullOrWhiteSpace(strongNameKey))
+	                writerParameters.StrongNameKeyBlob = File.ReadAllBytes(strongNameKey);
+                refAsm?.Write(refOutput, refWriterParameters);
+
                 return new CompileResult(true, true);
             }
             catch (Exception ex)
@@ -122,6 +139,7 @@ namespace Avalonia.Build.Tasks
             if (avares.Resources.Count(CheckXamlName) == 0)
                 // Nothing to do
                 return null;
+
             if (typeSystem.FindType("System.Reflection.AssemblyMetadataAttribute") is {} asmMetadata)
             {
                 var ctor = asm.MainModule.ImportReference(typeSystem.GetTypeReference(asmMetadata).Resolve()
@@ -131,14 +149,14 @@ namespace Avalonia.Build.Tasks
                 var arg2 = new CustomAttributeArgument(strType, defaultCompileBindings.ToString());
                 asm.CustomAttributes.Add(new CustomAttribute(ctor) { ConstructorArguments = { arg1, arg2 } });
             }
-            
-            var clrPropertiesDef = new TypeDefinition("CompiledAvaloniaXaml", "XamlIlHelpers",
+
+            var clrPropertiesDef = new TypeDefinition(CompiledAvaloniaXamlNamespace, "XamlIlHelpers",
                 TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
             asm.MainModule.Types.Add(clrPropertiesDef);
-            var indexerAccessorClosure = new TypeDefinition("CompiledAvaloniaXaml", "!IndexerAccessorFactoryClosure",
+            var indexerAccessorClosure = new TypeDefinition(CompiledAvaloniaXamlNamespace, "!IndexerAccessorFactoryClosure",
                 TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
             asm.MainModule.Types.Add(indexerAccessorClosure);
-            var trampolineBuilder = new TypeDefinition("CompiledAvaloniaXaml", "XamlIlTrampolines",
+            var trampolineBuilder = new TypeDefinition(CompiledAvaloniaXamlNamespace, "XamlIlTrampolines",
                 TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
             asm.MainModule.Types.Add(trampolineBuilder);
 
@@ -154,7 +172,7 @@ namespace Avalonia.Build.Tasks
                 new DeterministicIdGenerator());
 
 
-            var contextDef = new TypeDefinition("CompiledAvaloniaXaml", "XamlIlContext", 
+            var contextDef = new TypeDefinition(CompiledAvaloniaXamlNamespace, "XamlIlContext", 
                 TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
             asm.MainModule.Types.Add(contextDef);
 
@@ -175,8 +193,8 @@ namespace Avalonia.Build.Tasks
                 typeSystem.GetTypeReference(runtimeHelpers).Resolve().Methods
                     .First(x => x.Name == "CreateRootServiceProviderV2"));
             
-            var loaderDispatcherDef = new TypeDefinition("CompiledAvaloniaXaml", "!XamlLoader",
-                TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
+            var loaderDispatcherDef = new TypeDefinition(CompiledAvaloniaXamlNamespace, "!XamlLoader",
+                TypeAttributes.Class | TypeAttributes.Public, asm.MainModule.TypeSystem.Object);
 
 
             loaderDispatcherDef.CustomAttributes.Add(new CustomAttribute(editorBrowsableCtor)
@@ -204,8 +222,8 @@ namespace Avalonia.Build.Tasks
             
             bool CompileGroup(IResourceGroup group)
             {
-                var typeDef = new TypeDefinition("CompiledAvaloniaXaml", "!"+ group.Name,
-                    TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
+                var typeDef = new TypeDefinition(CompiledAvaloniaXamlNamespace, "!"+ group.Name,
+                    TypeAttributes.Class | TypeAttributes.Public, asm.MainModule.TypeSystem.Object);
 
                 typeDef.CustomAttributes.Add(new CustomAttribute(editorBrowsableCtor)
                 {
@@ -213,7 +231,9 @@ namespace Avalonia.Build.Tasks
                 });
                 asm.MainModule.Types.Add(typeDef);
                 var builder = typeSystem.CreateTypeBuilder(typeDef);
-                
+
+                var populateMethodsToTransform = new List<(MethodDefinition populateMethod, string resourceFilePath)>();
+
                 foreach (var res in group.Resources.Where(CheckXamlName).OrderBy(x=>x.FilePath.ToLowerInvariant()))
                 {
                     try
@@ -279,13 +299,24 @@ namespace Avalonia.Build.Tasks
                                 populateBuilder.DefineDelegateSubType(closureName, false, returnType, parameterTypes),
                             res.Uri, res
                         );
-                        
-                        
+
+                        var compiledPopulateMethod = typeSystem.GetTypeReference(populateBuilder).Resolve()
+                            .Methods.First(m => m.Name == populateName);
+
+                        // Include populate method and all nested methods/closures used in the populate method,
+                        // So we can replace style/resource includes in all of them. 
+                        populateMethodsToTransform.Add((compiledPopulateMethod, res.FilePath));
+                        populateMethodsToTransform.AddRange(compiledPopulateMethod.Body.Instructions
+                            .Where(b => b.OpCode == OpCodes.Call || b.OpCode == OpCodes.Callvirt || b.OpCode == OpCodes.Ldftn)
+                            .Select(b => b.Operand)
+                            .OfType<MethodReference>()
+                            .Where(m => compiledPopulateMethod.DeclaringType.NestedTypes.Contains(m.DeclaringType))
+                            .Select(m => m.Resolve())
+                            .Where(m => m.HasBody)
+                            .Select(m => (m, res.FilePath)));
+
                         if (classTypeDefinition != null)
                         {
-                            var compiledPopulateMethod = typeSystem.GetTypeReference(populateBuilder).Resolve()
-                                .Methods.First(m => m.Name == populateName);
-
                             var designLoaderFieldType = typeSystem
                                 .GetType("System.Action`1")
                                 .MakeGenericType(typeSystem.GetType("System.Object"));
@@ -435,8 +466,15 @@ namespace Avalonia.Build.Tasks
                     }
                     res.Remove();
                 }
-                
-                
+
+                foreach (var (populateMethod, resourceFilePath) in populateMethodsToTransform)
+                {
+                    if (!TransformXamlIncludes(engine, typeSystem, populateMethod, resourceFilePath, createRootServiceProviderMethod))
+                    {
+                        return false;
+                    }
+                }
+
                 // Technically that's a hack, but it fixes corert incompatibility caused by deterministic builds
                 int dupeCounter = 1;
                 foreach (var grp in typeDef.NestedTypes.GroupBy(x => x.Name))
@@ -463,6 +501,166 @@ namespace Avalonia.Build.Tasks
             loaderDispatcherMethod.Body.Instructions.Add(Instruction.Create(OpCodes.Ret));
             return true;
         }
+
+        static bool? CompileCoreForRefAssembly(
+            IBuildEngine engine, CecilTypeSystem sourceTypeSystem, CecilTypeSystem refTypeSystem)
+        {
+            var asm = refTypeSystem.TargetAssemblyDefinition;
+
+            var compiledTypes = sourceTypeSystem.TargetAssemblyDefinition.MainModule.Types
+                .Where(t => t.Namespace.StartsWith(CompiledAvaloniaXamlNamespace) && t.IsPublic).ToArray();
+            if (compiledTypes.Length == 0)
+            {
+                return null;
+            }
+
+            try
+            {
+                foreach (var ogType in compiledTypes)
+                {
+                    var wrappedOgType = sourceTypeSystem.TargetAssembly.FindType(ogType.FullName);
+                    
+                    var clrPropertiesDef = new TypeDefinition(ogType.Namespace, ogType.Name,
+                        TypeAttributes.Class | TypeAttributes.Public, asm.MainModule.TypeSystem.Object);
+                    asm.MainModule.Types.Add(clrPropertiesDef);
+                    foreach (var attribute in ogType.CustomAttributes)
+                    {
+                        var method = asm.MainModule.ImportReference(attribute.Constructor);
+                        clrPropertiesDef.CustomAttributes.Add(new CustomAttribute(method, attribute.GetBlob()));
+                    }
+
+                    var typeBuilder = refTypeSystem.CreateTypeBuilder(clrPropertiesDef);
+                    foreach (var ogMethod in wrappedOgType.Methods.Where(m => m.IsPublic && m.IsStatic))
+                    {
+                        var method = typeBuilder.DefineMethod(ogMethod.ReturnType, ogMethod.Parameters, ogMethod.Name,
+                            ogMethod.IsPublic, ogMethod.IsStatic, false);
+                        method.Generator.Ldnull();
+                        method.Generator.Throw();
+                    }
+
+                    typeBuilder.CreateType();
+                }
+            }
+            catch (Exception e)
+            {
+                engine.LogErrorEvent(new BuildErrorEventArgs("Avalonia", "XAMLIL", "",
+                    0, 0, 0, 0,
+                    e.Message, "", "Avalonia"));
+                return false;
+            }
+
+            return true;
+        }
         
+        private static bool TransformXamlIncludes(
+            IBuildEngine engine, CecilTypeSystem typeSystem,
+            MethodDefinition populateMethod, string resourceFilePath,
+            MethodReference createRootServiceProviderMethod)
+        {
+            var asm = typeSystem.TargetAssemblyDefinition;
+            foreach (var instruction in populateMethod.Body.Instructions.ToArray())
+            {
+                const string resolveStyleIncludeName = "ResolveStyleInclude";
+                const string resolveResourceInclude = "ResolveResourceInclude";
+                if (instruction.OpCode == OpCodes.Call
+                    && instruction.Operand is MethodReference
+                    {
+                        Name: resolveStyleIncludeName or resolveResourceInclude,
+                        DeclaringType: { Name: "XamlIlRuntimeHelpers" }
+                    })
+                {
+                    int lineNumber = 0, linePosition = 0;
+                    bool instructionsModified = false;
+                    try
+                    {
+                        var assetSource = (string)instruction.Previous.Previous.Previous.Operand;
+                        lineNumber = GetConstValue(instruction.Previous.Previous);
+                        linePosition = GetConstValue(instruction.Previous);
+
+                        var index = populateMethod.Body.Instructions.IndexOf(instruction);
+
+                        assetSource = assetSource.Replace("avares://", "");
+
+                        var assemblyNameSeparator = assetSource.IndexOf('/');
+                        var fileNameSeparator = assetSource.LastIndexOf('/');
+                        if (assemblyNameSeparator < 0 || fileNameSeparator < 0)
+                        {
+                            throw new InvalidProgramException(
+                                $"Invalid asset source \"{assetSource}\". It must contain assembly name and relative path.");
+                        }
+
+                        var assetAssemblyName = assetSource.Substring(0, assemblyNameSeparator);
+                        var assetAssembly = typeSystem.FindAssembly(assetAssemblyName)
+                                            ?? throw new InvalidProgramException(
+                                                $"Unable to resolve assembly \"{assetAssemblyName}\"");
+
+                        var fileName = Path.GetFileNameWithoutExtension(assetSource.Replace('/', '.'));
+                        if (assetAssembly.FindType(fileName) is { } type
+                            && type.FindConstructor() is { } ctor)
+                        {
+                            var ctorMethod =
+                                asm.MainModule.ImportReference(typeSystem.GetMethodReference(ctor));
+                            instructionsModified = true;
+                            populateMethod.Body.Instructions[index - 3] = Instruction.Create(OpCodes.Nop);
+                            populateMethod.Body.Instructions[index - 2] = Instruction.Create(OpCodes.Nop);
+                            populateMethod.Body.Instructions[index - 1] = Instruction.Create(OpCodes.Nop);
+                            populateMethod.Body.Instructions[index] = Instruction.Create(OpCodes.Newobj, ctorMethod);
+                        }
+                        else
+                        {
+                            var resources = assetAssembly.FindType("CompiledAvaloniaXaml.!AvaloniaResources")
+                                            ?? throw new InvalidOperationException(
+                                                $"Unable to resolve \"!AvaloniaResources\" type on \"{assetAssemblyName}\" assembly");
+
+                            var relativeName = "Build:" + assetSource.Substring(assemblyNameSeparator);
+                            var buildMethod = resources.FindMethod(m => m.Name == relativeName)
+                                              ?? throw new InvalidOperationException(
+                                                  $"Unable to resolve build method \"{relativeName}\" resource on the \"{assetAssemblyName}\" assembly");
+
+                            var methodReference = asm.MainModule.ImportReference(typeSystem.GetMethodReference(buildMethod));
+                            instructionsModified = true;
+                            populateMethod.Body.Instructions[index - 3] = Instruction.Create(OpCodes.Nop);
+                            populateMethod.Body.Instructions[index - 2] = Instruction.Create(OpCodes.Nop);
+                            populateMethod.Body.Instructions[index - 1] =
+                                Instruction.Create(OpCodes.Call, createRootServiceProviderMethod);
+                            populateMethod.Body.Instructions[index] = Instruction.Create(OpCodes.Call, methodReference);
+                        }
+                    }
+                    catch (Exception e)
+                    {
+                        if (instructionsModified)
+                        {
+                            engine.LogErrorEvent(new BuildErrorEventArgs("Avalonia", "XAMLIL", resourceFilePath,
+                                lineNumber, linePosition, lineNumber, linePosition,
+                                e.Message, "", "Avalonia"));
+                            return false;
+                        }
+                        else
+                        {
+                            engine.LogWarningEvent(new BuildWarningEventArgs("Avalonia", "XAMLIL",
+                                resourceFilePath,
+                                lineNumber, linePosition, lineNumber, linePosition,
+                                e.Message, "", "Avalonia"));
+                        }
+                    }
+                    
+                    static int GetConstValue(Instruction instruction)
+                    {
+                        if (instruction.OpCode is { Code : >= Code.Ldc_I4_0 and <= Code.Ldc_I4_8 })
+                        {
+                            return instruction.OpCode.Code - Code.Ldc_I4_0;
+                        }
+                        if (instruction.Operand is not null)
+                        {
+                            return Convert.ToInt32(instruction.Operand);
+                        }
+
+                        return 0;
+                    }
+                }
+            }
+
+            return true;
+        }
     }
 }

+ 3 - 6
src/Avalonia.Themes.Fluent/Accents/AccentColors.xaml

@@ -1,7 +1,5 @@
-<Style xmlns="https://github.com/avaloniaui"
-       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-       xmlns:sys="clr-namespace:System;assembly=netstandard">
-  <Style.Resources>
+<ResourceDictionary xmlns="https://github.com/avaloniaui"
+                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
     <!-- Accent Colours -->
     <!-- TODO pull accents from system... algorithm to generate shades -->
     <Color x:Key="SystemAccentColor">#FF0078D7</Color>
@@ -11,5 +9,4 @@
     <Color x:Key="SystemAccentColorLight1">#FF429CE3</Color>
     <Color x:Key="SystemAccentColorLight2">#FF76B9ED</Color>
     <Color x:Key="SystemAccentColorLight3">#FFA6D8FF</Color>
-  </Style.Resources>
-</Style>
+</ResourceDictionary>

+ 5 - 7
src/Avalonia.Themes.Fluent/Accents/Base.xaml

@@ -1,8 +1,7 @@
-<Style xmlns="https://github.com/avaloniaui"
-       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-       xmlns:sys="using:System"
-       xmlns:converters="using:Avalonia.Controls.Converters">
-  <Style.Resources>
+<ResourceDictionary xmlns="https://github.com/avaloniaui"
+                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+                    xmlns:sys="using:System"
+                    xmlns:converters="using:Avalonia.Controls.Converters">
     <!-- https://docs.microsoft.com/en-us/previous-versions/windows/apps/dn518235(v=win.10)?redirectedfrom=MSDN -->
     <!-- SystemColor Color Resources (Reflect OS High Contrast Settings) -->
     <Color x:Key="SystemColorButtonFaceColor">#FFF0F0F0</Color>
@@ -36,5 +35,4 @@
     <converters:CornerRadiusFilterConverter x:Key="RightCornerRadiusFilterConverter" Filter="TopRight, BottomRight"/>
     <converters:CornerRadiusFilterConverter x:Key="BottomCornerRadiusFilterConverter" Filter="BottomLeft, BottomRight"/>
     <converters:CornerRadiusFilterConverter x:Key="LeftCornerRadiusFilterConverter" Filter="TopLeft, BottomLeft"/>
-  </Style.Resources>
-</Style>
+</ResourceDictionary>

+ 4 - 7
src/Avalonia.Themes.Fluent/Accents/BaseDark.xaml

@@ -1,7 +1,5 @@
-<Style xmlns="https://github.com/avaloniaui"
-       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-       xmlns:sys="clr-namespace:System;assembly=netstandard">
-  <Style.Resources>
+<ResourceDictionary xmlns="https://github.com/avaloniaui"
+                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
     <!-- System Control Colors -->
     <Color x:Key="SystemAltHighColor">#FF000000</Color>
     <Color x:Key="SystemAltLowColor">#33000000</Color>
@@ -175,6 +173,5 @@
     <Thickness x:Key="MenuFlyoutScrollerMargin">0,4,0,4</Thickness>
 
     <!-- Moved from Menu.xaml -->
-    <Thickness x:Key="MenuBarItemPadding">12,0,12,0</Thickness> 
-  </Style.Resources>
-</Style>
+    <Thickness x:Key="MenuBarItemPadding">12,0,12,0</Thickness>
+</ResourceDictionary>

+ 3 - 6
src/Avalonia.Themes.Fluent/Accents/BaseLight.xaml

@@ -1,7 +1,5 @@
-<Style xmlns="https://github.com/avaloniaui"
-       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-       xmlns:sys="clr-namespace:System;assembly=netstandard">
-  <Style.Resources>
+<ResourceDictionary xmlns="https://github.com/avaloniaui"
+                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
     <!-- System Control Colors -->
     <Color x:Key="SystemAltHighColor">#FFFFFFFF</Color>
     <Color x:Key="SystemAltLowColor">#33FFFFFF</Color>
@@ -179,5 +177,4 @@
 
     <!-- Moved from Menu.xaml -->
     <Thickness x:Key="MenuBarItemPadding">12,0,12,0</Thickness>
-  </Style.Resources>
-</Style>
+</ResourceDictionary>

+ 4 - 7
src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml

@@ -1,7 +1,5 @@
-<Style xmlns="https://github.com/avaloniaui"
-  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-  xmlns:sys="clr-namespace:System;assembly=netstandard">
-  <Style.Resources>
+<ResourceDictionary xmlns="https://github.com/avaloniaui"
+                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
     <!-- Resources for Button.xaml -->
     <StaticResource x:Key="AccentButtonBackground" ResourceKey="SystemControlBackgroundAccentBrush" />
     <StaticResource x:Key="AccentButtonBackgroundPointerOver" ResourceKey="SystemAccentColorLight1" />
@@ -637,6 +635,5 @@
     <StaticResource x:Key="FlyoutBorderThemeBrush" ResourceKey="SystemControlTransientBorderBrush" />
 
     <!-- BaseResources for ScrollViewer.xaml -->
-    <SolidColorBrush x:Key="ScrollViewerScrollBarsSeparatorBackground" Color="{StaticResource SystemChromeMediumColor}" Opacity="0.9" />    
-  </Style.Resources>
-</Style>
+    <SolidColorBrush x:Key="ScrollViewerScrollBarsSeparatorBackground" Color="{StaticResource SystemChromeMediumColor}" Opacity="0.9" />
+</ResourceDictionary>

+ 3 - 6
src/Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml

@@ -1,7 +1,5 @@
-<Style xmlns="https://github.com/avaloniaui"
-  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-  xmlns:sys="clr-namespace:System;assembly=netstandard">
-  <Style.Resources>
+<ResourceDictionary xmlns="https://github.com/avaloniaui"
+                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
     <!-- Resources for Button.xaml -->
     <StaticResource x:Key="AccentButtonBackground" ResourceKey="SystemControlBackgroundAccentBrush" />
     <StaticResource x:Key="AccentButtonBackgroundPointerOver" ResourceKey="SystemAccentColorLight1" />
@@ -633,5 +631,4 @@
     
     <!-- Resources for ScrollViewer.xaml -->
     <SolidColorBrush x:Key="ScrollViewerScrollBarsSeparatorBackground" Color="{StaticResource SystemChromeMediumColor}" Opacity="0.9" />
-  </Style.Resources>
-</Style>
+</ResourceDictionary>

+ 1 - 0
src/Avalonia.Themes.Fluent/Avalonia.Themes.Fluent.csproj

@@ -10,6 +10,7 @@
     <AvaloniaResource Include="**/*.xaml" />
     <AvaloniaResource Include="Assets\*" />  
   </ItemGroup>
+  <Import Project="..\..\build\NullableEnable.props" />
   <Import Project="..\..\build\BuildTargets.targets" />
   <Import Project="..\..\build\Rx.props" />
   <Import Project="..\..\build\ApiDiff.props" />

+ 0 - 1
src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml

@@ -1,5 +1,4 @@
 <Styles
-    x:Class="Avalonia.Themes.Fluent.Controls.FluentControls"
     xmlns="https://github.com/avaloniaui"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
     <Styles.Resources>

+ 0 - 12
src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml.cs

@@ -1,12 +0,0 @@
-using Avalonia.Markup.Xaml;
-using Avalonia.Styling;
-
-namespace Avalonia.Themes.Fluent.Controls
-{
-    /// <summary>
-    /// The default Avalonia theme.
-    /// </summary>
-    public class FluentControls : Styles
-    {
-    }
-}

+ 1 - 2
src/Avalonia.Themes.Fluent/Controls/NativeMenuBar.xaml

@@ -1,13 +1,12 @@
 <ResourceDictionary xmlns="https://github.com/avaloniaui"
                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                     xmlns:local="using:Avalonia.Themes.Fluent">
-  <local:InverseBooleanValueConverter x:Key="AvaloniaThemesFluentNativeMenuBarInverseBooleanValueConverter" Default="True"/>
   <local:IBitmapToImageConverter x:Key="AvaloniaThemesFluentNativeMenuBarIBitmapToImageConverter"/>
   <ControlTheme x:Key="{x:Type NativeMenuBar}" TargetType="NativeMenuBar">
     <Setter Property="Template">
       <ControlTemplate>
         <Menu
-          IsVisible="{Binding $parent[TopLevel].(NativeMenu.IsNativeMenuExported), Converter={StaticResource AvaloniaThemesFluentNativeMenuBarInverseBooleanValueConverter}}"
+          IsVisible="{Binding !$parent[TopLevel].(NativeMenu.IsNativeMenuExported)}"
           Items="{Binding $parent[TopLevel].(NativeMenu.Menu).Items}">
           <Menu.Styles>
             <!-- Don't use x:DataType and compiled bindings here, as it might crash https://github.com/AvaloniaUI/Avalonia/pull/7954  -->

+ 15 - 17
src/Avalonia.Themes.Fluent/DensityStyles/Compact.xaml

@@ -3,21 +3,19 @@
     <Style Selector="TextBlock" >
         <Setter Property="FontSize" Value="14" />
     </Style>
-    <Style>
-        <Style.Resources>
-            <x:Double x:Key="ControlContentThemeFontSize">14</x:Double>
-            <x:Double x:Key="ContentControlFontSize">14</x:Double>
-            <x:Double x:Key="TextControlThemeMinHeight">24</x:Double>
-            <Thickness x:Key="TextControlThemePadding">2,2,6,1</Thickness>
-            <x:Double x:Key="ListViewItemMinHeight">32</x:Double>
-            <x:Double x:Key="TreeViewItemMinHeight">24</x:Double>
-            <Thickness x:Key="TimePickerHostPadding">0,1,0,2</Thickness>
-            <Thickness x:Key="DatePickerHostPadding">0,1,0,2</Thickness>
-            <Thickness x:Key="DatePickerHostMonthPadding">9,0,0,1</Thickness>
-            <Thickness x:Key="ComboBoxEditableTextPadding">10,0,30,0</Thickness>
-            <x:Double x:Key="ComboBoxMinHeight">24</x:Double>
-            <Thickness x:Key="ComboBoxPadding">12,1,0,3</Thickness>
-            <x:Double x:Key="NavigationViewItemOnLeftMinHeight">32</x:Double>
-        </Style.Resources>
-    </Style>
+    <Styles.Resources>
+      <x:Double x:Key="ControlContentThemeFontSize">14</x:Double>
+      <x:Double x:Key="ContentControlFontSize">14</x:Double>
+      <x:Double x:Key="TextControlThemeMinHeight">24</x:Double>
+      <Thickness x:Key="TextControlThemePadding">2,2,6,1</Thickness>
+      <x:Double x:Key="ListViewItemMinHeight">32</x:Double>
+      <x:Double x:Key="TreeViewItemMinHeight">24</x:Double>
+      <Thickness x:Key="TimePickerHostPadding">0,1,0,2</Thickness>
+      <Thickness x:Key="DatePickerHostPadding">0,1,0,2</Thickness>
+      <Thickness x:Key="DatePickerHostMonthPadding">9,0,0,1</Thickness>
+      <Thickness x:Key="ComboBoxEditableTextPadding">10,0,30,0</Thickness>
+      <x:Double x:Key="ComboBoxMinHeight">24</x:Double>
+      <Thickness x:Key="ComboBoxPadding">12,1,0,3</Thickness>
+      <x:Double x:Key="NavigationViewItemOnLeftMinHeight">32</x:Double>
+    </Styles.Resources>
 </Styles>

+ 0 - 9
src/Avalonia.Themes.Fluent/FluentDark.xaml

@@ -1,9 +0,0 @@
-<Styles xmlns="https://github.com/avaloniaui"
-       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-       xmlns:sys="using:System">
-  <StyleInclude Source="avares://Avalonia.Themes.Fluent/Accents/AccentColors.xaml" />
-  <StyleInclude Source="avares://Avalonia.Themes.Fluent/Accents/BaseDark.xaml" />
-  <StyleInclude Source="avares://Avalonia.Themes.Fluent/Accents/Base.xaml" />
-  <StyleInclude Source="avares://Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml" />
-  <StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/FluentControls.xaml" />
-</Styles>

+ 0 - 9
src/Avalonia.Themes.Fluent/FluentLight.xaml

@@ -1,9 +0,0 @@
-<Styles xmlns="https://github.com/avaloniaui"
-       xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-       xmlns:sys="using:System">
-  <StyleInclude Source="avares://Avalonia.Themes.Fluent/Accents/AccentColors.xaml" />
-  <StyleInclude Source="avares://Avalonia.Themes.Fluent/Accents/BaseLight.xaml" />
-  <StyleInclude Source="avares://Avalonia.Themes.Fluent/Accents/Base.xaml" />  
-  <StyleInclude Source="avares://Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml" />
-  <StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/FluentControls.xaml" />
-</Styles>

+ 0 - 244
src/Avalonia.Themes.Fluent/FluentTheme.cs

@@ -1,244 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Avalonia.Controls;
-using Avalonia.Markup.Xaml;
-using Avalonia.Markup.Xaml.Styling;
-using Avalonia.Styling;
-
-#nullable enable
-
-namespace Avalonia.Themes.Fluent
-{
-    public enum FluentThemeMode
-    {
-        Light,
-        Dark,
-    }
-
-    public enum DensityStyle
-    {
-        Normal,
-        Compact
-    }
-
-    /// <summary>
-    /// Includes the fluent theme in an application.
-    /// </summary>
-    public class FluentTheme : AvaloniaObject, IStyle, IResourceProvider
-    {
-        private readonly Uri _baseUri;
-        private Styles _fluentDark = new();
-        private Styles _fluentLight = new();
-        private Styles _sharedStyles = new();
-        private Styles _densityStyles = new();
-        private bool _isLoading;
-        private IStyle? _loaded;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="FluentTheme"/> class.
-        /// </summary>
-        /// <param name="baseUri">The base URL for the XAML context.</param>
-        public FluentTheme(Uri baseUri)
-        {
-            _baseUri = baseUri;
-            InitStyles(baseUri);
-        }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="FluentTheme"/> class.
-        /// </summary>
-        /// <param name="serviceProvider">The XAML service provider.</param>
-        public FluentTheme(IServiceProvider serviceProvider)
-        {
-            var ctx  = serviceProvider.GetService(typeof(IUriContext)) as IUriContext
-                 ?? throw new NullReferenceException("Unable retrive UriContext");
-            _baseUri = ctx.BaseUri;
-            InitStyles(_baseUri);
-        }
-
-        public static readonly StyledProperty<FluentThemeMode> ModeProperty =
-            AvaloniaProperty.Register<FluentTheme, FluentThemeMode>(nameof(Mode));
-
-        public static readonly StyledProperty<DensityStyle> DensityStyleProperty =
-            AvaloniaProperty.Register<FluentTheme, DensityStyle>(nameof(DensityStyle));
-
-        /// <summary>
-        /// Gets or sets the mode of the fluent theme (light, dark).
-        /// </summary>
-        public FluentThemeMode Mode
-        {
-            get => GetValue(ModeProperty);
-            set => SetValue(ModeProperty, value);
-        }
-
-        /// <summary>
-        /// Gets or sets the density style of the fluent theme (normal, compact).
-        /// </summary>
-        public DensityStyle DensityStyle
-        {
-            get => GetValue(DensityStyleProperty);
-            set => SetValue(DensityStyleProperty, value);
-        }
-        
-        protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
-        {
-            base.OnPropertyChanged(change);
-            
-            if (_loaded is null)
-            {
-                // If style wasn't yet loaded, no need to change children styles,
-                // it will be applied later in Loaded getter.
-                return;
-            }
-            
-            if (change.Property == ModeProperty)
-            {
-                if (Mode == FluentThemeMode.Dark)
-                {
-                    (Loaded as Styles)![1] = _fluentDark[0];
-                    (Loaded as Styles)![2] = _fluentDark[1];
-                }
-                else
-                {
-                    (Loaded as Styles)![1] = _fluentLight[0];
-                    (Loaded as Styles)![2] = _fluentLight[1];
-                }
-            }
-
-            if (change.Property == DensityStyleProperty)
-            {
-                if (DensityStyle == DensityStyle.Compact)
-                {
-                    (Loaded as Styles)!.Add(_densityStyles[0]);
-                }
-                else if (DensityStyle == DensityStyle.Normal)
-                {
-                    (Loaded as Styles)!.Remove(_densityStyles[0]);
-                }
-            }
-        }
-
-        public IResourceHost? Owner => (Loaded as IResourceProvider)?.Owner;
-
-        /// <summary>
-        /// Gets the loaded style.
-        /// </summary>
-        public IStyle Loaded
-        {
-            get
-            {
-                if (_loaded == null)
-                {
-                    _isLoading = true;
-
-                    if (Mode == FluentThemeMode.Light)
-                    {
-                        _loaded = new Styles() { _sharedStyles , _fluentLight[0], _fluentLight[1] };
-                    }
-                    else if (Mode == FluentThemeMode.Dark)
-                    {
-                        _loaded = new Styles() { _sharedStyles, _fluentDark[0], _fluentDark[1] };
-                    }
-
-                    if (DensityStyle == DensityStyle.Compact)
-                    {
-                        (_loaded as Styles)!.Add(_densityStyles[0]);
-                    }
-
-                    _isLoading = false;
-                }
-
-                return _loaded!;
-            }
-        }
-
-        bool IResourceNode.HasResources => (Loaded as IResourceProvider)?.HasResources ?? false;
-
-        IReadOnlyList<IStyle> IStyle.Children => _loaded?.Children ?? Array.Empty<IStyle>();
-
-        public event EventHandler? OwnerChanged
-        {
-            add
-            {
-                if (Loaded is IResourceProvider rp)
-                {
-                    rp.OwnerChanged += value;
-                }
-            }
-            remove
-            {
-                if (Loaded is IResourceProvider rp)
-                {
-                    rp.OwnerChanged -= value;
-                }
-            }
-        }
-
-        public SelectorMatchResult TryAttach(IStyleable target, object? host) => Loaded.TryAttach(target, host);
-
-        public bool TryGetResource(object key, out object? value)
-        {
-            if (!_isLoading && Loaded is IResourceProvider p)
-            {
-                return p.TryGetResource(key, out value);
-            }
-
-            value = null;
-            return false;
-        }
-
-        void IResourceProvider.AddOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.AddOwner(owner);
-        void IResourceProvider.RemoveOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.RemoveOwner(owner);
-
-        private void InitStyles(Uri baseUri)
-        {
-            _sharedStyles = new Styles
-            {
-                new StyleInclude(baseUri)
-                {
-                    Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/AccentColors.xaml")
-                },
-                new StyleInclude(baseUri)
-                {
-                    Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/Base.xaml")
-                },
-                new StyleInclude(baseUri)
-                {
-                    Source = new Uri("avares://Avalonia.Themes.Fluent/Controls/FluentControls.xaml")
-                }
-            };
-
-            _fluentLight = new Styles
-            {
-                new StyleInclude(baseUri)
-                {
-                    Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseLight.xaml")
-                },
-                new StyleInclude(baseUri)
-                {
-                    Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/FluentControlResourcesLight.xaml")
-                }
-            };
-
-            _fluentDark = new Styles
-            {
-                new StyleInclude(baseUri)
-                {
-                    Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/BaseDark.xaml")
-                },
-                new StyleInclude(baseUri)
-                {
-                    Source = new Uri("avares://Avalonia.Themes.Fluent/Accents/FluentControlResourcesDark.xaml")
-                }
-            };
-            
-            _densityStyles = new Styles
-            {
-                new StyleInclude(baseUri)
-                {
-                    Source = new Uri("avares://Avalonia.Themes.Fluent/DensityStyles/Compact.xaml")
-                }
-            };
-        }
-    }
-}

+ 21 - 0
src/Avalonia.Themes.Fluent/FluentTheme.xaml

@@ -0,0 +1,21 @@
+<Styles x:Class="Avalonia.Themes.Fluent.FluentTheme"
+        xmlns="https://github.com/avaloniaui"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
+  <Styles.Resources>
+    <ResourceDictionary>
+      <ResourceDictionary.MergedDictionaries>
+        <ResourceInclude Source="/Accents/AccentColors.xaml" />
+        <ResourceInclude Source="/Accents/Base.xaml" />
+      </ResourceDictionary.MergedDictionaries>
+      
+      <!-- These are not part of MergedDictionaries so we can add or remove them easier -->
+      <ResourceInclude x:Key="BaseDark" Source="/Accents/BaseDark.xaml" />
+      <ResourceInclude x:Key="FluentDark" Source="/Accents/FluentControlResourcesDark.xaml" />
+      <ResourceInclude x:Key="BaseLight" Source="/Accents/BaseLight.xaml" />
+      <ResourceInclude x:Key="FluentLight" Source="/Accents/FluentControlResourcesLight.xaml" />
+      <StyleInclude x:Key="CompactStyles" Source="/DensityStyles/Compact.xaml" />
+    </ResourceDictionary>
+  </Styles.Resources>
+  
+  <StyleInclude Source="/Controls/FluentControls.xaml" />
+</Styles>

+ 124 - 0
src/Avalonia.Themes.Fluent/FluentTheme.xaml.cs

@@ -0,0 +1,124 @@
+using System.Collections.Generic;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using Avalonia.Styling;
+
+namespace Avalonia.Themes.Fluent
+{
+    public enum FluentThemeMode
+    {
+        Light,
+        Dark,
+    }
+
+    public enum DensityStyle
+    {
+        Normal,
+        Compact
+    }
+
+    /// <summary>
+    /// Includes the fluent theme in an application.
+    /// </summary>
+    public class FluentTheme : Styles
+    {
+        private readonly IResourceDictionary _baseDark;
+        private readonly IResourceDictionary _fluentDark;
+        private readonly IResourceDictionary _baseLight;
+        private readonly IResourceDictionary _fluentLight;
+        private readonly Styles _compactStyles;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="FluentTheme"/> class.
+        /// </summary>
+        public FluentTheme()
+        {
+            AvaloniaXamlLoader.Load(this);
+            
+            _baseDark = (IResourceDictionary)GetAndRemove("BaseDark");
+            _fluentDark = (IResourceDictionary)GetAndRemove("FluentDark");
+            _baseLight = (IResourceDictionary)GetAndRemove("BaseLight");
+            _fluentLight = (IResourceDictionary)GetAndRemove("FluentLight");
+            _compactStyles = (Styles)GetAndRemove("CompactStyles");
+            
+            EnsureThemeVariants();
+            EnsureCompactStyles();
+
+            object GetAndRemove(string key)
+            {
+                var val = Resources[key]
+                          ?? throw new KeyNotFoundException($"Key {key} was not found in the resources");
+                Resources.Remove(key);
+                return val;
+            }
+        }
+
+        public static readonly StyledProperty<FluentThemeMode> ModeProperty =
+            AvaloniaProperty.Register<FluentTheme, FluentThemeMode>(nameof(Mode));
+
+        public static readonly StyledProperty<DensityStyle> DensityStyleProperty =
+            AvaloniaProperty.Register<FluentTheme, DensityStyle>(nameof(DensityStyle));
+
+        /// <summary>
+        /// Gets or sets the mode of the fluent theme (light, dark).
+        /// </summary>
+        public FluentThemeMode Mode
+        {
+            get => GetValue(ModeProperty);
+            set => SetValue(ModeProperty, value);
+        }
+
+        /// <summary>
+        /// Gets or sets the density style of the fluent theme (normal, compact).
+        /// </summary>
+        public DensityStyle DensityStyle
+        {
+            get => GetValue(DensityStyleProperty);
+            set => SetValue(DensityStyleProperty, value);
+        }
+        
+        protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+        {
+            base.OnPropertyChanged(change);
+            
+            if (change.Property == ModeProperty)
+            {
+                EnsureThemeVariants();
+            }
+
+            if (change.Property == DensityStyleProperty)
+            {
+                EnsureCompactStyles();
+            }
+        }
+
+        private void EnsureThemeVariants()
+        {
+            var themeVariantResource1 = Mode == FluentThemeMode.Dark ? _baseDark : _baseLight;
+            var themeVariantResource2 = Mode == FluentThemeMode.Dark ? _fluentDark : _fluentLight;
+            var dict = Resources.MergedDictionaries;
+            if (dict.Count == 2)
+            {
+                dict.Insert(1, themeVariantResource1);
+                dict.Add(themeVariantResource2);
+            }
+            else
+            {
+                dict[1] = themeVariantResource1;
+                dict[3] = themeVariantResource2;
+            }
+        }
+
+        private void EnsureCompactStyles()
+        {
+            if (DensityStyle == DensityStyle.Compact)
+            {
+                Add(_compactStyles);
+            }
+            else
+            {
+                Remove(_compactStyles);
+            }
+        }
+    }
+}

+ 2 - 6
src/Avalonia.Themes.Fluent/IBitmapToImageConverter.cs

@@ -1,9 +1,5 @@
 using System;
-using System.Collections.Generic;
 using System.Globalization;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 using Avalonia.Controls;
 using Avalonia.Data.Converters;
 using Avalonia.Media.Imaging;
@@ -12,7 +8,7 @@ namespace Avalonia.Themes.Fluent
 {
     internal class IBitmapToImageConverter : IValueConverter
     {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
         {
             if (value != null && value is IBitmap bm)
                 return new Image { Source=bm };
@@ -20,7 +16,7 @@ namespace Avalonia.Themes.Fluent
             return null;
         }
 
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
         {
             throw new NotImplementedException();
         }

+ 0 - 20
src/Avalonia.Themes.Fluent/InverseBooleanValueConverter.cs

@@ -1,20 +0,0 @@
-using System;
-using System.Globalization;
-using Avalonia.Data.Converters;
-
-namespace Avalonia.Themes.Fluent
-{
-    class InverseBooleanValueConverter : IValueConverter
-    {
-        public bool Default { get; set; }
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            return value is bool b ? !b : Default;
-        }
-
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            return value is bool b ? !b : !Default;
-        }
-    }
-}

+ 3 - 5
src/Avalonia.Themes.Simple/Accents/Base.xaml

@@ -1,8 +1,7 @@
-<Style
+<ResourceDictionary
     xmlns="https://github.com/avaloniaui"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-    xmlns:sys="clr-namespace:System;assembly=netstandard">
-    <Style.Resources>
+    xmlns:sys="using:System">
         <Color x:Key="ThemeAccentColor">#CC119EDA</Color>
         <Color x:Key="ThemeAccentColor2">#99119EDA</Color>
         <Color x:Key="ThemeAccentColor3">#66119EDA</Color>
@@ -60,5 +59,4 @@
 
         <sys:Double x:Key="IconElementThemeHeight">20</sys:Double>
         <sys:Double x:Key="IconElementThemeWidth">20</sys:Double>
-    </Style.Resources>
-</Style>
+</ResourceDictionary>

+ 3 - 6
src/Avalonia.Themes.Simple/Accents/BaseDark.xaml

@@ -1,8 +1,6 @@
-<Style
+<ResourceDictionary
     xmlns="https://github.com/avaloniaui"
-    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-    xmlns:sys="clr-namespace:System;assembly=netstandard">
-    <Style.Resources>
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
 
         <Color x:Key="ThemeBackgroundColor">#FF282828</Color>
         <Color x:Key="ThemeBorderLowColor">#FF505050</Color>
@@ -34,5 +32,4 @@
         <SolidColorBrush x:Key="ThemeForegroundBrush" Color="{StaticResource ThemeForegroundColor}" />
         <SolidColorBrush x:Key="HighlightBrush" Color="{StaticResource HighlightColor}" />
 
-    </Style.Resources>
-</Style>
+</ResourceDictionary>

+ 3 - 6
src/Avalonia.Themes.Simple/Accents/BaseLight.xaml

@@ -1,8 +1,6 @@
-<Style
+<ResourceDictionary
     xmlns="https://github.com/avaloniaui"
-    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-    xmlns:sys="clr-namespace:System;assembly=netstandard">
-    <Style.Resources>
+    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
 
         <Color x:Key="ThemeBackgroundColor">#FFFFFFFF</Color>
         <Color x:Key="ThemeBorderLowColor">#FFAAAAAA</Color>
@@ -34,5 +32,4 @@
         <SolidColorBrush x:Key="ThemeForegroundBrush" Color="{StaticResource ThemeForegroundColor}" />
 
         <SolidColorBrush x:Key="HighlightBrush" Color="{StaticResource HighlightColor}" />
-    </Style.Resources>
-</Style>
+</ResourceDictionary>

+ 1 - 0
src/Avalonia.Themes.Simple/Avalonia.Themes.Simple.csproj

@@ -9,6 +9,7 @@
     <ProjectReference Include="..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />
     <AvaloniaResource Include="**/*.xaml" />
   </ItemGroup>
+  <Import Project="..\..\build\NullableEnable.props" />
   <Import Project="..\..\build\BuildTargets.targets" />
   <Import Project="..\..\build\Rx.props" />
   <Import Project="..\..\build\ApiDiff.props" />

+ 1 - 3
src/Avalonia.Themes.Simple/Controls/NativeMenuBar.xaml

@@ -1,14 +1,12 @@
 <ResourceDictionary xmlns="https://github.com/avaloniaui"
                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                     xmlns:default="using:Avalonia.Themes.Simple">
-  <default:InverseBooleanValueConverter x:Key="AvaloniaThemesSimpleNativeMenuBarInverseBooleanValueConverter"
-                                        Default="True" />
   <default:IBitmapToImageConverter x:Key="AvaloniaThemesSimpleNativeMenuBarIBitmapToImageConverter" />
   <ControlTheme x:Key="{x:Type NativeMenuBar}"
                 TargetType="NativeMenuBar">
     <Setter Property="Template">
       <ControlTemplate>
-        <Menu IsVisible="{Binding $parent[TopLevel].(NativeMenu.IsNativeMenuExported), Converter={StaticResource AvaloniaThemesSimpleNativeMenuBarInverseBooleanValueConverter}}"
+        <Menu IsVisible="{Binding !$parent[TopLevel].(NativeMenu.IsNativeMenuExported)}"
               Items="{Binding $parent[TopLevel].(NativeMenu.Menu).Items}">
           <Menu.Styles>
             <!-- Don't use x:DataType and compiled bindings here, as it might crash https://github.com/AvaloniaUI/Avalonia/pull/7954  -->

+ 2 - 2
src/Avalonia.Themes.Simple/IBitmapToImageConverter.cs

@@ -12,7 +12,7 @@ namespace Avalonia.Themes.Simple
 {
     internal class IBitmapToImageConverter : IValueConverter
     {
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+        public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
         {
             if (value != null && value is IBitmap bm)
                 return new Image { Source=bm };
@@ -20,7 +20,7 @@ namespace Avalonia.Themes.Simple
             return null;
         }
 
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
+        public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
         {
             throw new NotImplementedException();
         }

+ 0 - 20
src/Avalonia.Themes.Simple/InverseBooleanValueConverter.cs

@@ -1,20 +0,0 @@
-using System;
-using System.Globalization;
-using Avalonia.Data.Converters;
-
-namespace Avalonia.Themes.Simple
-{
-    class InverseBooleanValueConverter : IValueConverter
-    {
-        public bool Default { get; set; }
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            return value is bool b ? !b : Default;
-        }
-
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            return value is bool b ? !b : !Default;
-        }
-    }
-}

+ 0 - 166
src/Avalonia.Themes.Simple/SimpleTheme.cs

@@ -1,166 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Avalonia.Controls;
-using Avalonia.Markup.Xaml;
-using Avalonia.Markup.Xaml.Styling;
-using Avalonia.Styling;
-#nullable enable
-
-namespace Avalonia.Themes.Simple
-{
-    public class SimpleTheme : AvaloniaObject, IStyle, IResourceProvider
-    {
-        public static readonly StyledProperty<SimpleThemeMode> ModeProperty =
-        AvaloniaProperty.Register<SimpleTheme, SimpleThemeMode>(nameof(Mode));
-
-        private readonly Uri _baseUri;
-        private bool _isLoading;
-        private IStyle? _loaded;
-        private Styles _sharedStyles = new();
-        private Styles _simpleDark = new();
-        private Styles _simpleLight = new();
-        /// <summary>
-        /// Initializes a new instance of the <see cref="SimpleTheme"/> class.
-        /// </summary>
-        /// <param name="baseUri">The base URL for the XAML context.</param>
-        public SimpleTheme(Uri? baseUri = null)
-        {
-            _baseUri = baseUri ?? new Uri("avares://Avalonia.Themes.Simple/");
-            InitStyles(_baseUri);
-        }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="SimpleTheme"/> class.
-        /// </summary>
-        /// <param name="serviceProvider">The XAML service provider.</param>
-        public SimpleTheme(IServiceProvider serviceProvider)
-        {
-            var service = serviceProvider.GetService(typeof(IUriContext));
-            if (service == null)
-            {
-                throw new Exception("There is no service object of type IUriContext!");
-            }
-            _baseUri = ((IUriContext)service).BaseUri;
-            InitStyles(_baseUri);
-        }
-
-        public event EventHandler? OwnerChanged
-        {
-            add
-            {
-                if (Loaded is IResourceProvider rp)
-                {
-                    rp.OwnerChanged += value;
-                }
-            }
-            remove
-            {
-                if (Loaded is IResourceProvider rp)
-                {
-                    rp.OwnerChanged -= value;
-                }
-            }
-        }
-
-        IReadOnlyList<IStyle> IStyle.Children => _loaded?.Children ?? Array.Empty<IStyle>();
-
-        bool IResourceNode.HasResources => (Loaded as IResourceProvider)?.HasResources ?? false;
-
-        public IStyle Loaded
-        {
-            get
-            {
-                if (_loaded == null)
-                {
-                    _isLoading = true;
-
-                    if (Mode == SimpleThemeMode.Light)
-                    {
-                        _loaded = new Styles { _sharedStyles, _simpleLight };
-                    }
-                    else if (Mode == SimpleThemeMode.Dark)
-                    {
-                        _loaded = new Styles { _sharedStyles, _simpleDark };
-                    }
-                    _isLoading = false;
-                }
-
-                return _loaded!;
-            }
-        }
-
-        /// <summary>
-        /// Gets or sets the mode of the fluent theme (light, dark).
-        /// </summary>
-        public SimpleThemeMode Mode
-        {
-            get => GetValue(ModeProperty);
-            set => SetValue(ModeProperty, value);
-        }
-        public IResourceHost? Owner => (Loaded as IResourceProvider)?.Owner;
-
-        void IResourceProvider.AddOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.AddOwner(owner);
-
-        void IResourceProvider.RemoveOwner(IResourceHost owner) => (Loaded as IResourceProvider)?.RemoveOwner(owner);
-
-        public SelectorMatchResult TryAttach(IStyleable target, object? host) => Loaded.TryAttach(target, host);
-
-        public bool TryGetResource(object key, out object? value)
-        {
-            if (!_isLoading && Loaded is IResourceProvider p)
-            {
-                return p.TryGetResource(key, out value);
-            }
-
-            value = null;
-            return false;
-        }
-
-        protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
-        {
-            base.OnPropertyChanged(change);
-            if (change.Property == ModeProperty)
-            {
-                if (Mode == SimpleThemeMode.Dark)
-                {
-                    (Loaded as Styles)![1] = _simpleDark[0];
-                }
-                else
-                {
-                    (Loaded as Styles)![1] = _simpleLight[0];
-                }
-            }
-        }
-
-        private void InitStyles(Uri baseUri)
-        {
-            _sharedStyles = new Styles
-            {
-                new StyleInclude(baseUri)
-                {
-                    Source = new Uri("avares://Avalonia.Themes.Simple/Controls/SimpleControls.xaml")
-                },
-                new StyleInclude(baseUri)
-                {
-                    Source = new Uri("avares://Avalonia.Themes.Simple/Accents/Base.xaml")
-                }
-            };
-            _simpleLight = new Styles
-            {
-                new StyleInclude(baseUri)
-                {
-                    Source = new Uri("avares://Avalonia.Themes.Simple/Accents/BaseLight.xaml")
-                }
-            };
-
-            _simpleDark = new Styles
-            {
-                new StyleInclude(baseUri)
-                {
-                    Source = new Uri("avares://Avalonia.Themes.Simple/Accents/BaseDark.xaml")
-                }
-            };
-        }
-
-    }
-}

+ 17 - 0
src/Avalonia.Themes.Simple/SimpleTheme.xaml

@@ -0,0 +1,17 @@
+<Styles x:Class="Avalonia.Themes.Simple.SimpleTheme"
+        xmlns="https://github.com/avaloniaui"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
+  <Styles.Resources>
+    <ResourceDictionary>
+      <ResourceDictionary.MergedDictionaries>
+        <ResourceInclude Source="/Accents/Base.xaml" />  
+      </ResourceDictionary.MergedDictionaries>
+      
+      <!-- These are not part of MergedDictionaries so we can add or remove them easier -->
+      <ResourceInclude x:Key="BaseDark" Source="/Accents/BaseDark.xaml" />
+      <ResourceInclude x:Key="BaseLight" Source="/Accents/BaseLight.xaml" />
+    </ResourceDictionary>
+  </Styles.Resources>
+  
+  <StyleInclude Source="/Controls/SimpleControls.xaml" />
+</Styles>

+ 69 - 0
src/Avalonia.Themes.Simple/SimpleTheme.xaml.cs

@@ -0,0 +1,69 @@
+using System.Collections.Generic;
+using Avalonia.Controls;
+using Avalonia.Markup.Xaml;
+using Avalonia.Styling;
+
+namespace Avalonia.Themes.Simple
+{
+    public class SimpleTheme : Styles
+    {
+        public static readonly StyledProperty<SimpleThemeMode> ModeProperty =
+            AvaloniaProperty.Register<SimpleTheme, SimpleThemeMode>(nameof(Mode));
+
+        private readonly IResourceDictionary _simpleDark;
+        private readonly IResourceDictionary _simpleLight;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="SimpleTheme"/> class.
+        /// </summary>
+        public SimpleTheme()
+        {
+            AvaloniaXamlLoader.Load(this);
+
+            _simpleDark = (IResourceDictionary)GetAndRemove("BaseDark");
+            _simpleLight = (IResourceDictionary)GetAndRemove("BaseLight");
+            EnsureThemeVariant();
+
+            object GetAndRemove(string key)
+            {
+                var val = Resources[key]
+                    ?? throw new KeyNotFoundException($"Key {key} was not found in the resources");
+                Resources.Remove(key);
+                return val;
+            }
+        }
+
+        /// <summary>
+        /// Gets or sets the mode of the fluent theme (light, dark).
+        /// </summary>
+        public SimpleThemeMode Mode
+        {
+            get => GetValue(ModeProperty);
+            set => SetValue(ModeProperty, value);
+        }
+ 
+        protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
+        {
+            base.OnPropertyChanged(change);
+
+            if (change.Property == ModeProperty)
+            {
+                EnsureThemeVariant();
+            }
+        }
+
+        private void EnsureThemeVariant()
+        {
+            var themeVariantResource = Mode == SimpleThemeMode.Dark ? _simpleDark : _simpleLight;
+            var dict = Resources.MergedDictionaries;
+            if (dict.Count == 1)
+            {
+                dict.Add(themeVariantResource);
+            }
+            else
+            {
+                dict[1] = themeVariantResource;
+            }
+        }
+    }
+}

+ 2 - 1
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompiler.cs

@@ -56,7 +56,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
                 new AvaloniaXamlIlSetterTransformer(),
                 new AvaloniaXamlIlConstructorServiceProviderTransformer(),
                 new AvaloniaXamlIlTransitionsTypeMetadataTransformer(),
-                new AvaloniaXamlIlResolveByNameMarkupExtensionReplacer()
+                new AvaloniaXamlIlResolveByNameMarkupExtensionReplacer(),
+                new AvaloniaXamlIlAssetIncludeTransformer()
             );
             InsertBefore<ConvertPropertyValuesToAssignmentsTransformer>(
                 new AvaloniaXamlIlOptionMarkupExtensionTransformer());

+ 93 - 0
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlAssetIncludeTransformer.cs

@@ -0,0 +1,93 @@
+using System.Linq;
+using XamlX;
+using XamlX.Ast;
+using XamlX.Emit;
+using XamlX.IL;
+using XamlX.Transform;
+using XamlX.TypeSystem;
+
+namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
+
+internal class AvaloniaXamlIlAssetIncludeTransformer : IXamlAstTransformer
+{
+    public IXamlAstNode Transform(AstTransformationContext context, IXamlAstNode node)
+    {
+        if (node is not XamlAstObjectNode objectNode
+            || (objectNode.Type.GetClrType() != context.GetAvaloniaTypes().StyleInclude
+                && objectNode.Type.GetClrType() != context.GetAvaloniaTypes().ResourceInclude))
+        {
+            return node;
+        }
+
+        var nodeTypeName = objectNode.Type.GetClrType().Name;
+
+        var sourceProperty = objectNode.Children.OfType<XamlAstXamlPropertyValueNode>().FirstOrDefault(n => n.Property.GetClrProperty().Name == "Source");
+        var directives = objectNode.Children.OfType<XamlAstXmlDirective>().ToList();
+        if (sourceProperty is null
+            || objectNode.Children.Count != (directives.Count + 1))
+        {
+            throw new XamlParseException($"Unexpected property on the {nodeTypeName} node", node);
+        }
+
+        if (sourceProperty.Values.OfType<XamlAstTextNode>().FirstOrDefault() is not { } sourceTextNode)
+        {
+            // TODO: make it a compiler warning
+            // Source value can be set with markup extension instead of a text node, we don't support it here yet.
+            return node;
+        }
+
+        var originalAssetPath = sourceTextNode.Text;
+        if (!(originalAssetPath.StartsWith("avares://") || originalAssetPath.StartsWith("/")))
+        {
+            return node;
+        }
+
+        var runtimeHelpers = context.GetAvaloniaTypes().RuntimeHelpers;
+        var markerMethodName = "Resolve" + nodeTypeName;
+        var markerMethod = runtimeHelpers.FindMethod(m => m.Name == markerMethodName && m.Parameters.Count == 3);
+        if (markerMethod is null)
+        {
+            throw new XamlParseException($"Marker method \"{markerMethodName}\" was not found for the \"{nodeTypeName}\" node", node);
+        }
+        
+        return new XamlValueWithManipulationNode(
+            node,
+            new AssetIncludeMethodNode(node, markerMethod, originalAssetPath),
+            new XamlManipulationGroupNode(node, directives));
+    }
+
+    private class AssetIncludeMethodNode : XamlAstNode, IXamlAstValueNode, IXamlAstILEmitableNode
+    {
+        private readonly IXamlMethod _method;
+        private readonly string _originalAssetPath;
+
+        public AssetIncludeMethodNode(
+            IXamlAstNode original, IXamlMethod method, string originalAssetPath)
+            : base(original)
+        {
+            _method = method;
+            _originalAssetPath = originalAssetPath;
+        }
+
+        public IXamlAstTypeReference Type => new XamlAstClrTypeReference(this, _method.ReturnType, false);
+
+        public XamlILNodeEmitResult Emit(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlILEmitter codeGen)
+        {
+            var absoluteSource = _originalAssetPath;
+            if (absoluteSource.StartsWith("/"))
+            {
+                // Avoid Uri class here to avoid potential problems with escaping.
+                // Keeping string as close to the original as possible.
+                var absoluteBaseUrl =  context.RuntimeContext.BaseUrl;
+                absoluteSource = absoluteBaseUrl.Substring(0, absoluteBaseUrl.LastIndexOf('/')) + absoluteSource;
+            }
+
+            codeGen.Ldstr(absoluteSource);
+            codeGen.Ldc_I4(Line);
+            codeGen.Ldc_I4(Position);
+            codeGen.EmitCall(_method);
+
+            return XamlILNodeEmitResult.Type(0, _method.ReturnType);
+        }
+    }
+}

+ 6 - 0
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

@@ -102,6 +102,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
         public IXamlType TextDecorations { get; }
         public IXamlType TextTrimming { get; }
         public IXamlType ISetter { get; }
+        public IXamlType IStyle { get; }
+        public IXamlType StyleInclude { get; }
+        public IXamlType ResourceInclude { get; }
         public IXamlType IResourceDictionary { get; }
         public IXamlType ResourceDictionary { get; }
         public IXamlMethod ResourceDictionaryDeferredAdd { get; }
@@ -232,6 +235,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
             TextDecorations = cfg.TypeSystem.GetType("Avalonia.Media.TextDecorations");
             TextTrimming = cfg.TypeSystem.GetType("Avalonia.Media.TextTrimming");
             ISetter = cfg.TypeSystem.GetType("Avalonia.Styling.ISetter");
+            IStyle = cfg.TypeSystem.GetType("Avalonia.Styling.IStyle");
+            StyleInclude = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Styling.StyleInclude");
+            ResourceInclude = cfg.TypeSystem.GetType("Avalonia.Markup.Xaml.Styling.ResourceInclude");
             IResourceDictionary = cfg.TypeSystem.GetType("Avalonia.Controls.IResourceDictionary");
             ResourceDictionary = cfg.TypeSystem.GetType("Avalonia.Controls.ResourceDictionary");
             ResourceDictionaryDeferredAdd = ResourceDictionary.FindMethod("AddDeferred", XamlIlTypes.Void, true, XamlIlTypes.Object,

+ 1 - 2
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@@ -33,9 +33,7 @@
         <Compile Include="MarkupExtensions\OnFormFactorExtension.cs" />
         <Compile Include="MarkupExtensions\OnPlatformExtension.cs" />
         <Compile Include="MarkupExtensions\ResolveByNameExtension.cs" />
-        <Compile Include="MarkupExtensions\ResourceInclude.cs" />
         <Compile Include="MarkupExtensions\StaticResourceExtension.cs" />
-        <Compile Include="MarkupExtensions\StyleIncludeExtension.cs" />
         <Compile Include="Parsers\PropertyParser.cs" />
         <Compile Include="Converters\BitmapTypeConverter.cs" />
         <Compile Include="Converters\IconTypeConverter.cs" />
@@ -45,6 +43,7 @@
         <Compile Include="MarkupExtensions\RelativeSourceExtension.cs" />
         <Compile Include="Properties\AssemblyInfo.cs" />
         <Compile Include="RuntimeXamlLoaderConfiguration.cs" />
+        <Compile Include="Styling\ResourceInclude.cs" />
         <Compile Include="Styling\StyleInclude.cs" />
         <Compile Include="Templates\ControlTemplate.cs" />
         <Compile Include="Templates\DataTemplate.cs" />

+ 0 - 22
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/StyleIncludeExtension.cs

@@ -1,22 +0,0 @@
-using Avalonia.Markup.Xaml.Styling;
-using Avalonia.Styling;
-using System.ComponentModel;
-using System;
-
-namespace Avalonia.Markup.Xaml.MarkupExtensions
-{
-    public class StyleIncludeExtension
-    {
-        public StyleIncludeExtension()
-        {
-        }
-
-        public IStyle ProvideValue(IServiceProvider serviceProvider)
-        {
-            return new StyleInclude(serviceProvider.GetContextBaseUri()) { Source = Source };
-        }
-
-        public Uri Source { get; set; }
-
-    }
-}

+ 20 - 10
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/ResourceInclude.cs → src/Markup/Avalonia.Markup.Xaml/Styling/ResourceInclude.cs

@@ -1,20 +1,37 @@
 using System;
-using System.ComponentModel;
 using Avalonia.Controls;
 
 #nullable enable
 
-namespace Avalonia.Markup.Xaml.MarkupExtensions
+namespace Avalonia.Markup.Xaml.Styling
 {
     /// <summary>
     /// Loads a resource dictionary from a specified URL.
     /// </summary>
     public class ResourceInclude : IResourceProvider
     {
-        private Uri? _baseUri;
+        private readonly Uri? _baseUri;
         private IResourceDictionary? _loaded;
         private bool _isLoading;
 
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ResourceInclude"/> class.
+        /// </summary>
+        /// <param name="baseUri">The base URL for the XAML context.</param>
+        public ResourceInclude(Uri? baseUri)
+        {
+            _baseUri = baseUri;
+        }
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="ResourceInclude"/> class.
+        /// </summary>
+        /// <param name="serviceProvider">The XAML service provider.</param>
+        public ResourceInclude(IServiceProvider serviceProvider)
+        {
+            _baseUri = serviceProvider.GetContextBaseUri();
+        }
+
         /// <summary>
         /// Gets the loaded resource dictionary.
         /// </summary>
@@ -61,12 +78,5 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions
 
         void IResourceProvider.AddOwner(IResourceHost owner) => Loaded.AddOwner(owner);
         void IResourceProvider.RemoveOwner(IResourceHost owner) => Loaded.RemoveOwner(owner);
-
-        public ResourceInclude ProvideValue(IServiceProvider serviceProvider)
-        {
-            var tdc = (ITypeDescriptorContext)serviceProvider;
-            _baseUri = tdc?.GetContextBaseUri();
-            return this;
-        }
     }
 }

+ 2 - 2
src/Markup/Avalonia.Markup.Xaml/Styling/StyleInclude.cs

@@ -12,7 +12,7 @@ namespace Avalonia.Markup.Xaml.Styling
     /// </summary>
     public class StyleInclude : IStyle, IResourceProvider
     {
-        private readonly Uri _baseUri;
+        private readonly Uri? _baseUri;
         private IStyle[]? _loaded;
         private bool _isLoading;
 
@@ -20,7 +20,7 @@ namespace Avalonia.Markup.Xaml.Styling
         /// Initializes a new instance of the <see cref="StyleInclude"/> class.
         /// </summary>
         /// <param name="baseUri">The base URL for the XAML context.</param>
-        public StyleInclude(Uri baseUri)
+        public StyleInclude(Uri? baseUri)
         {
             _baseUri = baseUri;
         }

+ 13 - 0
src/Markup/Avalonia.Markup.Xaml/XamlIl/Runtime/XamlIlRuntimeHelpers.cs

@@ -5,7 +5,10 @@ using System.Reflection;
 using Avalonia.Controls;
 using Avalonia.Controls.Templates;
 using Avalonia.Data;
+using Avalonia.Markup.Xaml.MarkupExtensions;
+using Avalonia.Markup.Xaml.Styling;
 using Avalonia.Platform;
+using Avalonia.Styling;
 
 // ReSharper disable UnusedMember.Global
 // ReSharper disable UnusedParameter.Global
@@ -14,6 +17,16 @@ namespace Avalonia.Markup.Xaml.XamlIl.Runtime
 {
     public static class XamlIlRuntimeHelpers
     {
+        public static IStyle ResolveStyleInclude(string absoluteSource, int line, int position)
+        {
+            return new StyleInclude((Uri)null) { Source = new Uri(absoluteSource) }.Loaded;
+        }
+
+        public static IResourceDictionary ResolveResourceInclude(string absoluteSource, int line, int position)
+        {
+            return new ResourceInclude((Uri)null) { Source = new Uri(absoluteSource) }.Loaded;
+        }
+
         public static Func<IServiceProvider, object> DeferredTransformationFactoryV1(Func<IServiceProvider, object> builder,
             IServiceProvider provider)
         {

+ 1 - 1
tests/Avalonia.Benchmarks/Themes/FluentBenchmark.cs

@@ -61,7 +61,7 @@ namespace Avalonia.Benchmarks.Themes
             AssetLoader.RegisterResUriParsers();
             return new Styles
             {
-                new Avalonia.Themes.Fluent.FluentTheme(new Uri("avares://Avalonia.Benchmarks"))
+                new Avalonia.Themes.Fluent.FluentTheme()
                 {
 
                 }

+ 2 - 2
tests/Avalonia.Benchmarks/Themes/ThemeBenchmark.cs

@@ -31,7 +31,7 @@ namespace Avalonia.Benchmarks.Themes
         [Arguments(FluentThemeMode.Light)]
         public bool InitFluentTheme(FluentThemeMode mode)
         {
-            UnitTestApplication.Current.Styles[0] = new FluentTheme(new Uri("resm:Styles?assembly=Avalonia.Benchmarks"))
+            UnitTestApplication.Current.Styles[0] = new FluentTheme()
             {
                 Mode = mode
             };
@@ -43,7 +43,7 @@ namespace Avalonia.Benchmarks.Themes
         [Arguments(SimpleThemeMode.Light)]
         public bool InitSimpleTheme(SimpleThemeMode mode)
         {
-            UnitTestApplication.Current.Styles[0] = new SimpleTheme(new Uri("resm:Styles?assembly=Avalonia.Benchmarks"))
+            UnitTestApplication.Current.Styles[0] = new SimpleTheme()
             {
                 Mode = mode
             };

+ 4 - 4
tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj

@@ -22,12 +22,12 @@
     <ProjectReference Include="..\Avalonia.UnitTests\Avalonia.UnitTests.csproj" />
   </ItemGroup>
   <ItemGroup>
-    <EmbeddedResource Include="Xaml\Style1.xaml">
+    <AvaloniaResource Include="Xaml\Style1.xaml">
       <SubType>Designer</SubType>
-    </EmbeddedResource>
-    <EmbeddedResource Include="Xaml\Style2.xaml">
+    </AvaloniaResource>
+    <AvaloniaResource Include="Xaml\Style2.xaml">
       <SubType>Designer</SubType>
-    </EmbeddedResource>
+    </AvaloniaResource>
     <AvaloniaResource Include="Xaml\XamlIlClassWithPrecompiledXaml.xaml" />
     <AvaloniaResource Include="Xaml\XamlIlClassWithCustomProperty.xaml" />
   </ItemGroup>

+ 1 - 5
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs

@@ -473,13 +473,9 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
 
                 Assert.True(styles.Count == 1);
 
-                var styleInclude = styles.First() as StyleInclude;
+                var styleInclude = styles.First() as IStyle;
 
                 Assert.NotNull(styleInclude);
-
-                var style = styleInclude.Loaded;
-
-                Assert.NotNull(style);
             }
         }
 

+ 23 - 9
tests/Avalonia.Markup.Xaml.UnitTests/Xaml/StyleTests.cs

@@ -113,27 +113,41 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
         public void StyleInclude_Is_Built()
         {
             using (UnitTestApplication.Start(TestServices.StyledWindow
-                                              .With(theme: () => new Styles())))
+                       .With(theme: () => new Styles())))
             {
                 var xaml = @"
 <ContentControl xmlns='https://github.com/avaloniaui'>
     <ContentControl.Styles>
-        <StyleInclude Source='resm:Avalonia.Markup.Xaml.UnitTests.Xaml.Style1.xaml?assembly=Avalonia.Markup.Xaml.UnitTests'/>
+        <StyleInclude Source='avares://Avalonia.Markup.Xaml.UnitTests/Xaml/Style1.xaml'/>
     </ContentControl.Styles>
 </ContentControl>";
 
                 var window = AvaloniaRuntimeXamlLoader.Parse<ContentControl>(xaml);
+                
+                Assert.IsType<Style>(window.Styles[0]);
+            }
+        }
+        
+        [Fact]
+        public void StyleInclude_Is_Built_Resources()
+        {
+            using (UnitTestApplication.Start(TestServices.StyledWindow
+                       .With(theme: () => new Styles())))
+            {
+                var xaml = @"
+<ContentControl xmlns='https://github.com/avaloniaui'
+                xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
+    <ContentControl.Resources>
+        <StyleInclude x:Key='Include' Source='avares://Avalonia.Markup.Xaml.UnitTests/Xaml/Style1.xaml'/>
+    </ContentControl.Resources>
+</ContentControl>";
 
-                Assert.Single(window.Styles);
-
-                var styleInclude = window.Styles[0] as StyleInclude;
+                var window = AvaloniaRuntimeXamlLoader.Parse<ContentControl>(xaml);
 
-                Assert.NotNull(styleInclude);
-                Assert.NotNull(styleInclude.Source);
-                Assert.NotNull(styleInclude.Loaded);
+                Assert.IsType<Style>(window.Resources["Include"]);
             }
         }
-
+        
         [Fact]
         public void Setter_Can_Contain_Template()
         {