Skip to content

Commit

Permalink
fix(theme): source dictionary not getting theme updated
Browse files Browse the repository at this point in the history
  • Loading branch information
Xiaoy312 committed Feb 2, 2022
1 parent 322bb9b commit 6027e66
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Markup;
using Windows.UI.Xaml.Media;

#if HAS_UNO
using Uno.UI.Xaml;
#endif

namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml
{
[TestClass]
public class Given_ResourceDictionary
{
{
[TestMethod]
[RunsOnUIThread]
public void When_Key_Overwritten()
Expand All @@ -20,5 +27,73 @@ public void When_Key_Overwritten()

Assert.AreEqual(newValue, resourceDictionary[key]);
}

#if HAS_UNO // uses uno specifics code
[TestMethod]
[RunsOnUIThread]
public void When_LinkedResDict_ThemeUpdated()
{
const string TestBrush = nameof(TestBrush);
const string TestThemeColor = nameof(TestThemeColor);
const string Light = nameof(Light);
const string Dark = nameof(Dark);

var theme = ResourceDictionary.GetActiveTheme();
try
{
// setup
ResourceDictionary.SetActiveTheme("Light");

// initialize source res-dict
var parserContext = new XamlParseContext();
var dontcare = new object();
var source = new ResourceDictionary()
{
IsParsing = true,
ThemeDictionaries =
{
[Light] = new WeakResourceInitializer(dontcare, that => new ResourceDictionary { [TestThemeColor] = Colors.Red, }),
[Dark] = new WeakResourceInitializer(dontcare, that => new ResourceDictionary { [TestThemeColor] = Colors.Blue, }),
},
[TestBrush] = new WeakResourceInitializer(dontcare, that =>
{
var brush = new SolidColorBrush();
ResourceResolverSingleton.Instance.ApplyResource(brush, SolidColorBrush.ColorProperty, TestThemeColor, true, parserContext);
return brush;
})
};
source.CreationComplete();

// making a copy from source
var copy = new ResourceDictionary();
copy.CopyFrom(source);

// retrieve the "TestBrush" from each res-dict while also materializing it in both the source and in the copy
var materialized1 = (SolidColorBrush)source[TestBrush];
var materialized2 = (SolidColorBrush)copy[TestBrush];
var materialized2Color = materialized2.Color;

// set active theme and update the copy res-dict
ResourceDictionary.SetActiveTheme(Dark);
copy.UpdateThemeBindings(Windows.UI.Xaml.Data.ResourceUpdateReason.ThemeResource);

// retrieve the "TestBrush" again from each res-dict
var materialized3 = (SolidColorBrush)source[TestBrush];
var materialized4 = (SolidColorBrush)copy[TestBrush];

// validation
Assert.AreEqual(false, ReferenceEquals(materialized1, materialized2)); // we are expecting these to be different, as the CopyFrom should copy them as WeakResourceInitializer...
Assert.AreEqual(false, ReferenceEquals(materialized3, materialized4)); // ^same
Assert.AreNotEqual(materialized2Color, materialized4.Color); // check the theme change is actually applied (otherwise it would void the next check)
Assert.AreEqual(materialized3.Color, materialized4.Color); // check the theme change is propagated to the source res-dict
}
finally
{
// clean up
ResourceDictionary.SetActiveTheme(theme);
}
}
#endif
}
}
14 changes: 14 additions & 0 deletions src/Uno.UI/UI/Xaml/ResourceDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
using ResourceKey = Windows.UI.Xaml.SpecializedResourceDictionary.ResourceKey;
using System.Runtime.CompilerServices;
using Windows.UI.Xaml.Data;
using Uno.UI.DataBinding;

namespace Windows.UI.Xaml
{
Expand All @@ -21,6 +22,7 @@ public partial class ResourceDictionary : DependencyObject, IDependencyObjectPar
private readonly SpecializedResourceDictionary _values = new SpecializedResourceDictionary();
private readonly List<ResourceDictionary> _mergedDictionaries = new List<ResourceDictionary>();
private ResourceDictionary _themeDictionaries;
private ManagedWeakReference _sourceDictionary;

/// <summary>
/// This event is fired when a key that has value of type <see cref="ResourceDictionary"/> is added or changed in the current <see cref="ResourceDictionary" />
Expand Down Expand Up @@ -424,6 +426,11 @@ internal void CopyFrom(ResourceDictionary source)
_mergedDictionaries.Clear();
_themeDictionaries?.Clear();

// In a foreign library that uses merged-dict with 'Source=...', there can be multiple instances of res-dict from one single xaml file.
// And, the instance referenced in App.xaml may not be the same one used in merged-dict of library-a\textbox.xaml.
// In order to ensure the theme updates covers all of them, we need to keep track of this source instance for theme updates before it is lost.
_sourceDictionary = WeakReferencePool.RentWeakReference(this, source);

_values.AddRange(source._values);
_mergedDictionaries.AddRange(source._mergedDictionaries);
if (source._themeDictionaries != null)
Expand Down Expand Up @@ -556,6 +563,11 @@ internal void UpdateThemeBindings(ResourceUpdateReason updateReason)
{
mergedDict.UpdateThemeBindings(updateReason);
}

if (_sourceDictionary?.Target is ResourceDictionary target)
{
target.UpdateThemeBindings(updateReason);
}
}

[EditorBrowsable(EditorBrowsableState.Never)]
Expand Down Expand Up @@ -596,6 +608,8 @@ public StaticResourceAliasRedirect(string resourceKey, XamlParseContext parseCon

internal static object GetStaticResourceAliasPassthrough(string resourceKey, XamlParseContext parseContext) => new StaticResourceAliasRedirect(resourceKey, parseContext);

internal static ResourceKey GetActiveTheme() => Themes.Active;

internal static void SetActiveTheme(SpecializedResourceDictionary.ResourceKey key)
=> Themes.Active = key;

Expand Down

0 comments on commit 6027e66

Please sign in to comment.