diff --git a/tests/Avalonia.Base.UnitTests/Styling/ResourceDictionaryTests.cs b/tests/Avalonia.Base.UnitTests/Styling/ResourceDictionaryTests.cs
index 86b1b897d4..5527eda6ee 100644
--- a/tests/Avalonia.Base.UnitTests/Styling/ResourceDictionaryTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Styling/ResourceDictionaryTests.cs
@@ -29,7 +29,7 @@ namespace Avalonia.Base.UnitTests.Styling
{ "foo", "bar" },
};
- Assert.True(target.TryGetResource("foo", out var result));
+ Assert.True(target.TryGetResource("foo", null, out var result));
Assert.Equal("bar", result);
}
@@ -47,7 +47,7 @@ namespace Avalonia.Base.UnitTests.Styling
}
};
- Assert.True(target.TryGetResource("foo", out var result));
+ Assert.True(target.TryGetResource("foo", null, out var result));
Assert.Equal("bar", result);
}
@@ -64,7 +64,7 @@ namespace Avalonia.Base.UnitTests.Styling
{ "foo", "baz" },
});
- Assert.True(target.TryGetResource("foo", out var result));
+ Assert.True(target.TryGetResource("foo", null, out var result));
Assert.Equal("bar", result);
}
@@ -86,7 +86,7 @@ namespace Avalonia.Base.UnitTests.Styling
}
};
- Assert.True(target.TryGetResource("foo", out var result));
+ Assert.True(target.TryGetResource("foo", null, out var result));
Assert.Equal("baz", result);
}
diff --git a/tests/Avalonia.Base.UnitTests/Styling/StylesTests.cs b/tests/Avalonia.Base.UnitTests/Styling/StylesTests.cs
index a6777c9466..c9fc86e205 100644
--- a/tests/Avalonia.Base.UnitTests/Styling/StylesTests.cs
+++ b/tests/Avalonia.Base.UnitTests/Styling/StylesTests.cs
@@ -108,7 +108,7 @@ namespace Avalonia.Base.UnitTests.Styling
}
};
- Assert.True(target.TryGetResource("foo", out var result));
+ Assert.True(target.TryGetResource("foo", ThemeVariant.Dark, out var result));
Assert.Equal("bar", result);
}
}
diff --git a/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs b/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs
index 59953f457a..bc47e68bc1 100644
--- a/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs
+++ b/tests/Avalonia.Benchmarks/Styling/ResourceBenchmarks.cs
@@ -44,7 +44,7 @@ namespace Avalonia.Benchmarks.Styling
return new Styles
{
preHost,
- new TestStyles(50, 3, 5),
+ new TestStyles(50, 3, 5, 0),
postHost
};
}
diff --git a/tests/Avalonia.Benchmarks/TestStyles.cs b/tests/Avalonia.Benchmarks/TestStyles.cs
index be2ad7d072..208f238101 100644
--- a/tests/Avalonia.Benchmarks/TestStyles.cs
+++ b/tests/Avalonia.Benchmarks/TestStyles.cs
@@ -1,10 +1,11 @@
-using Avalonia.Styling;
+using Avalonia.Controls;
+using Avalonia.Styling;
namespace Avalonia.Benchmarks
{
public class TestStyles : Styles
{
- public TestStyles(int childStylesCount, int childInnerStyleCount, int childResourceCount)
+ public TestStyles(int childStylesCount, int childInnerStyleCount, int childResourceCount, int childThemeResourcesCount)
{
for (int i = 0; i < childStylesCount; i++)
{
@@ -18,7 +19,19 @@ namespace Avalonia.Benchmarks
{
childStyle.Resources.Add($"resource.{i}.{j}.{k}", null);
}
-
+
+ if (childThemeResourcesCount > 0)
+ {
+ ResourceDictionary darkTheme, lightTheme;
+ childStyle.Resources.ThemeDictionaries[ThemeVariant.Dark] = darkTheme = new ResourceDictionary();
+ childStyle.Resources.ThemeDictionaries[ThemeVariant.Light] = lightTheme = new ResourceDictionary();
+ for (int k = 0; k < childThemeResourcesCount; k++)
+ {
+ darkTheme.Add($"resource.theme.{i}.{j}.{k}", null);
+ lightTheme.Add($"resource.theme.{i}.{j}.{k}", null);
+ }
+ }
+
childStyles.Add(childStyle);
}
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Converters/AvaloniaPropertyConverterTest.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/AvaloniaPropertyConverterTest.cs
index 9c2860eb26..d4d188f584 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Converters/AvaloniaPropertyConverterTest.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Converters/AvaloniaPropertyConverterTest.cs
@@ -142,6 +142,12 @@ namespace Avalonia.Markup.Xaml.UnitTests.Converters
throw new NotImplementedException();
}
+ public ThemeVariant ThemeVariant
+ {
+ get { throw new NotImplementedException(); }
+ }
+ public event EventHandler ThemeVariantChanged;
+
public void DetachStyles()
{
throw new NotImplementedException();
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs
index f2e1a99006..535b96420a 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/MarkupExtensions/DynamicResourceExtensionTests.cs
@@ -938,7 +938,7 @@ namespace Avalonia.Markup.Xaml.UnitTests.MarkupExtensions
public void AddOwner(IResourceHost owner) => Owner = owner;
public void RemoveOwner(IResourceHost owner) => Owner = null;
- public bool TryGetResource(object key, out object value)
+ public bool TryGetResource(object key, ThemeVariant themeVariant, out object value)
{
RequestedResources.Add(key);
value = key;
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/ThemeDictionariesTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/ThemeDictionariesTests.cs
new file mode 100644
index 0000000000..56040c2186
--- /dev/null
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/ThemeDictionariesTests.cs
@@ -0,0 +1,444 @@
+using Avalonia.Controls;
+using Avalonia.Markup.Data;
+using Avalonia.Markup.Xaml.MarkupExtensions;
+using Avalonia.Media;
+using Avalonia.Styling;
+using Moq;
+using Xunit;
+
+namespace Avalonia.Markup.Xaml.UnitTests;
+
+public class ThemeDictionariesTests : XamlTestBase
+{
+ [Fact]
+ public void DynamicResource_Updated_When_Control_Theme_Changed()
+ {
+ var themeVariantScope = (ThemeVariantScope)AvaloniaRuntimeXamlLoader.Load(@"
+
+
+
+
+
+ Black
+
+
+ White
+
+
+
+
+
+
+");
+ var border = (Border)themeVariantScope.Child!;
+
+ Assert.Equal(Colors.White, ((ISolidColorBrush)border.Background)!.Color);
+
+ themeVariantScope.RequestedThemeVariant = ThemeVariant.Dark;
+
+ Assert.Equal(Colors.Black, ((ISolidColorBrush)border.Background)!.Color);
+ }
+
+ [Fact]
+ public void DynamicResource_Updated_When_Control_Theme_Changed_No_Xaml()
+ {
+ var themeVariantScope = new ThemeVariantScope
+ {
+ RequestedThemeVariant = ThemeVariant.Light,
+ Resources = new ResourceDictionary
+ {
+ ThemeDictionaries =
+ {
+ [ThemeVariant.Dark] = new ResourceDictionary { ["DemoBackground"] = Brushes.Black },
+ [ThemeVariant.Light] = new ResourceDictionary { ["DemoBackground"] = Brushes.White }
+ }
+ },
+ Child = new Border()
+ };
+ var border = (Border)themeVariantScope.Child!;
+ border[!Border.BackgroundProperty] = new DynamicResourceExtension("DemoBackground");
+
+ DelayedBinding.ApplyBindings(border);
+
+ Assert.Equal(Colors.White, ((ISolidColorBrush)border.Background)!.Color);
+
+ themeVariantScope.RequestedThemeVariant = ThemeVariant.Dark;
+
+ Assert.Equal(Colors.Black, ((ISolidColorBrush)border.Background)!.Color);
+ }
+
+ [Fact]
+ public void Intermediate_DynamicResource_Updated_When_Control_Theme_Changed()
+ {
+ var themeVariantScope = (ThemeVariantScope)AvaloniaRuntimeXamlLoader.Load(@"
+
+
+
+
+
+ Black
+
+
+ White
+
+
+
+
+
+
+
+");
+ var border = (Border)themeVariantScope.Child!;
+
+ Assert.Equal(Colors.White, ((ISolidColorBrush)border.Background)!.Color);
+
+ themeVariantScope.RequestedThemeVariant = ThemeVariant.Dark;
+
+ Assert.Equal(Colors.Black, ((ISolidColorBrush)border.Background)!.Color);
+ }
+
+ [Fact]
+ public void Intermediate_StaticResource_Can_Be_Reached_From_ThemeDictionaries()
+ {
+ var themeVariantScope = (ThemeVariantScope)AvaloniaRuntimeXamlLoader.Load(@"
+
+
+
+
+
+ Black
+
+
+
+ White
+
+
+
+
+
+
+
+");
+ var border = (Border)themeVariantScope.Child!;
+
+ Assert.Equal(Colors.White, ((ISolidColorBrush)border.Background)!.Color);
+
+ themeVariantScope.RequestedThemeVariant = ThemeVariant.Dark;
+
+ Assert.Equal(Colors.Black, ((ISolidColorBrush)border.Background)!.Color);
+ }
+
+ [Fact(Skip = "Not implemented")]
+ public void StaticResource_Inside_Of_ThemeDictionaries_Should_Use_Same_Theme_Key()
+ {
+ var themeVariantScope = (ThemeVariantScope)AvaloniaRuntimeXamlLoader.Load(@"
+
+
+
+
+
+ Black
+
+
+ White
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+");
+ var border = (Border)themeVariantScope.Child!;
+
+ Assert.Equal(Colors.White, ((ISolidColorBrush)border.Background)!.Color);
+
+ themeVariantScope.RequestedThemeVariant = ThemeVariant.Dark;
+
+ Assert.Equal(Colors.Black, ((ISolidColorBrush)border.Background)!.Color);
+ }
+
+ [Fact]
+ public void StaticResource_Outside_Of_Dictionaries_Should_Use_Control_ThemeVariant()
+ {
+ using (AvaloniaLocator.EnterScope())
+ {
+ var applicationThemeHost = new Mock();
+ applicationThemeHost.SetupGet(h => h.ActualThemeVariant).Returns(ThemeVariant.Dark);
+ AvaloniaLocator.CurrentMutable.Bind().ToConstant(applicationThemeHost.Object);
+
+ var themeVariantScope = (ThemeVariantScope)AvaloniaRuntimeXamlLoader.Load(@"
+
+
+
+
+
+ Black
+
+
+ White
+
+
+
+
+
+
+");
+ var border = (Border)themeVariantScope.Child!;
+
+ themeVariantScope.RequestedThemeVariant = ThemeVariant.Light;
+ Assert.Equal(Colors.White, ((ISolidColorBrush)border.Background)!.Color);
+ }
+ }
+
+ [Fact]
+ public void Inner_ThemeDictionaries_Works_Properly()
+ {
+ var themeVariantScope = (ThemeVariantScope)AvaloniaRuntimeXamlLoader.Load(@"
+
+
+
+
+
+
+ Black
+
+
+ White
+
+
+
+
+
+");
+ var border = (Border)themeVariantScope.Child!;
+
+ Assert.Equal(Colors.White, ((ISolidColorBrush)border.Background)!.Color);
+
+ themeVariantScope.RequestedThemeVariant = ThemeVariant.Dark;
+
+ Assert.Equal(Colors.Black, ((ISolidColorBrush)border.Background)!.Color);
+ }
+
+ [Fact]
+ public void Inner_Resource_Can_Reference_Parent_ThemeDictionaries()
+ {
+ var themeVariantScope = (ThemeVariantScope)AvaloniaRuntimeXamlLoader.Load(@"
+
+
+
+
+
+ Black
+
+
+ White
+
+
+
+
+
+
+
+
+
+
+
+
+");
+ var border = (Border)themeVariantScope.Child!;
+
+ Assert.Equal(Colors.White, ((ISolidColorBrush)border.Background)!.Color);
+
+ themeVariantScope.RequestedThemeVariant = ThemeVariant.Dark;
+
+ Assert.Equal(Colors.Black, ((ISolidColorBrush)border.Background)!.Color);
+ }
+
+ [Fact]
+ public void DynamicResource_Can_Access_Resources_Outside_Of_ThemeDictionaries()
+ {
+ var themeVariantScope = (ThemeVariantScope)AvaloniaRuntimeXamlLoader.Load(@"
+
+
+
+
+
+
+
+
+
+
+
+ Black
+ White
+
+
+
+
+");
+ var border = (Border)themeVariantScope.Child!;
+
+ Assert.Equal(Colors.White, ((ISolidColorBrush)border.Background)!.Color);
+
+ themeVariantScope.RequestedThemeVariant = ThemeVariant.Dark;
+
+ Assert.Equal(Colors.Black, ((ISolidColorBrush)border.Background)!.Color);
+ }
+
+ [Fact]
+ public void Inner_Dictionary_Does_Not_Affect_Parent_Resources()
+ {
+ // It might be a nice feature, but neither Avalonia nor UWP supports it.
+ // Better to expect this limitation with a unit test.
+ var themeVariantScope = (ThemeVariantScope)AvaloniaRuntimeXamlLoader.Load(@"
+
+
+
+ Red
+
+
+
+
+
+
+
+
+
+ Black
+
+
+ White
+
+
+
+
+
+");
+ var border = (Border)themeVariantScope.Child!;
+
+ Assert.Equal(Colors.Red, ((ISolidColorBrush)border.Background)!.Color);
+
+ themeVariantScope.RequestedThemeVariant = ThemeVariant.Dark;
+
+ Assert.Equal(Colors.Red, ((ISolidColorBrush)border.Background)!.Color);
+ }
+
+ [Fact]
+ public void Custom_Theme_Can_Be_Defined_In_ThemeDictionaries()
+ {
+ var themeVariantScope = (ThemeVariantScope)AvaloniaRuntimeXamlLoader.Load(@"
+
+
+
+
+
+ Black
+
+
+ White
+
+
+ Pink
+
+
+
+
+
+
+");
+ var border = (Border)themeVariantScope.Child!;
+
+ themeVariantScope.RequestedThemeVariant = new ThemeVariant("Custom");
+
+ Assert.Equal(Colors.Pink, ((ISolidColorBrush)border.Background)!.Color);
+ }
+
+ [Fact]
+ public void Custom_Theme_Fallbacks_To_Inherit_Theme_DynamicResource()
+ {
+ var themeVariantScope = (ThemeVariantScope)AvaloniaRuntimeXamlLoader.Load(@"
+
+
+
+
+
+ Black
+
+
+
+
+
+");
+ var border = (Border)themeVariantScope.Child!;
+
+ themeVariantScope.RequestedThemeVariant = new ThemeVariant("Custom", ThemeVariant.Dark);
+
+ Assert.Equal(Colors.Black, ((ISolidColorBrush)border.Background)!.Color);
+ }
+
+ [Fact]
+ public void Custom_Theme_Fallbacks_To_Inherit_Theme_StaticResource()
+ {
+ var themeVariantScope = (ThemeVariantScope)AvaloniaRuntimeXamlLoader.Load(@"
+
+
+
+
+ Custom
+ Dark
+
+
+
+
+
+
+
+ Black
+
+
+
+
+
+
+");
+ var border = (Border)themeVariantScope.Child!;
+
+ Assert.Equal(Colors.Black, ((ISolidColorBrush)border.Background)!.Color);
+ }
+}
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
index 0cdc9ee3b1..c8be1c6d19 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs
@@ -448,13 +448,13 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
Assert.True(style.Resources.Count > 0);
- style.TryGetResource("Brush", out var brush);
+ style.TryGetResource("Brush", null, out var brush);
Assert.NotNull(brush);
Assert.IsAssignableFrom(brush);
Assert.Equal(Colors.White, ((ISolidColorBrush)brush).Color);
- style.TryGetResource("Double", out var d);
+ style.TryGetResource("Double", null, out var d);
Assert.Equal(10.0, d);
}
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/MergeResourceIncludeTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/MergeResourceIncludeTests.cs
index 92807b2cb9..aa76756069 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/MergeResourceIncludeTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/MergeResourceIncludeTests.cs
@@ -1,4 +1,6 @@
using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Xml;
using Avalonia.Controls;
@@ -128,4 +130,105 @@ public class MergeResourceIncludeTests
Assert.Equal(Colors.Black, ((ISolidColorBrush)resources["brush5"]!).Color);
Assert.Equal(Colors.White, ((ISolidColorBrush)resources["brush6"]!).Color);
}
+
+ [Fact]
+ public void MergeResourceInclude_Works_With_ThemeDictionaries()
+ {
+ var documents = new[]
+ {
+ new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Resources1.xaml"), @"
+
+
+
+ White
+ Black
+
+
+ Black
+ White
+
+
+"),
+ new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Resources2.xaml"), @"
+
+
+
+ Red
+ Blue
+
+
+ Blue
+ Red
+
+
+"),
+ new RuntimeXamlLoaderDocument(@"
+
+
+
+
+
+"),
+ };
+
+ var objects = AvaloniaRuntimeXamlLoader.LoadGroup(documents);
+ var resources = Assert.IsType(objects[2]);
+ Assert.Empty(resources.MergedDictionaries);
+
+ Assert.Equal(Colors.White, Get("brush1", ThemeVariant.Light).Color);
+ Assert.Equal(Colors.Black, Get("brush2", ThemeVariant.Light).Color);
+ Assert.Equal(Colors.Black, Get("brush1", ThemeVariant.Dark).Color);
+ Assert.Equal(Colors.White, Get("brush2", ThemeVariant.Dark).Color);
+
+ Assert.Equal(Colors.Red, Get("brush3", ThemeVariant.Light).Color);
+ Assert.Equal(Colors.Blue, Get("brush4", ThemeVariant.Light).Color);
+ Assert.Equal(Colors.Blue, Get("brush3", ThemeVariant.Dark).Color);
+ Assert.Equal(Colors.Red, Get("brush4", ThemeVariant.Dark).Color);
+
+ ISolidColorBrush Get(string key, ThemeVariant themeVariant)
+ {
+ return resources.TryGetResource(key, themeVariant, out var res) ?
+ (ISolidColorBrush)res! :
+ throw new KeyNotFoundException();
+ }
+ }
+
+ [Fact]
+ public void MergeResourceInclude_Fails_With_ThemeDictionaries_Duplicate_Resources()
+ {
+ var documents = new[]
+ {
+ new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Resources1.xaml"), @"
+
+
+
+ White
+
+
+"),
+ new RuntimeXamlLoaderDocument(new Uri("avares://Tests/Resources2.xaml"), @"
+
+
+
+ Black
+
+
+"),
+ new RuntimeXamlLoaderDocument(@"
+
+
+
+
+
+"),
+ };
+
+ Assert.ThrowsAny(() => AvaloniaRuntimeXamlLoader.LoadGroup(documents));
+ }
}
diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ResourceDictionaryTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ResourceDictionaryTests.cs
index d74d85e2bc..6cab83751f 100644
--- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ResourceDictionaryTests.cs
+++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/ResourceDictionaryTests.cs
@@ -276,6 +276,38 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml
}
}
+ [Fact]
+ public void Closest_Resource_Should_Be_Referenced()
+ {
+ using (StyledWindow())
+ {
+ var xaml = @"
+
+
+
+
+
+
+";
+ var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml);
+ var windowResources = (ResourceDictionary)window.Resources;
+ var buttonResources = (ResourceDictionary)((Button)window.Content!).Resources;
+
+ var brush = Assert.IsType(windowResources["Red2"]);
+ Assert.Equal(Colors.Red, brush.Color);
+
+ Assert.False(windowResources.ContainsDeferredKey("Red"));
+ Assert.False(windowResources.ContainsDeferredKey("Red2"));
+
+ Assert.True(buttonResources.ContainsDeferredKey("Red"));
+ }
+ }
+
private IDisposable StyledWindow(params (string, string)[] assets)
{
var services = TestServices.StyledWindow.With(
diff --git a/tests/Avalonia.UnitTests/TestServices.cs b/tests/Avalonia.UnitTests/TestServices.cs
index 40306a4513..339cb1462c 100644
--- a/tests/Avalonia.UnitTests/TestServices.cs
+++ b/tests/Avalonia.UnitTests/TestServices.cs
@@ -155,7 +155,7 @@ namespace Avalonia.UnitTests
private static IStyle CreateSimpleTheme()
{
- return new SimpleTheme { Mode = SimpleThemeMode.Light };
+ return new SimpleTheme();
}
private static IPlatformRenderInterface CreateRenderInterfaceMock()