From 5c93b56da905d19bb9c2202e332d6a99f126e8dd Mon Sep 17 00:00:00 2001 From: Stephane Delcroix Date: Wed, 17 Apr 2024 14:05:06 +0200 Subject: [PATCH 1/2] [C] Propagate resources when reparenting - fixes a bug when the theme is changed while the control/page isn't parented. Should fix @LeDahu22 reported issue of #21744 --- .../src/Core/Application/Application.cs | 4 +- src/Controls/src/Core/Element/Element.cs | 5 +- .../Xaml.UnitTests/Issues/Maui21774.xaml | 9 +++ .../Xaml.UnitTests/Issues/Maui21774.xaml.cs | 69 +++++++++++++++++++ 4 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 src/Controls/tests/Xaml.UnitTests/Issues/Maui21774.xaml create mode 100644 src/Controls/tests/Xaml.UnitTests/Issues/Maui21774.xaml.cs diff --git a/src/Controls/src/Core/Application/Application.cs b/src/Controls/src/Core/Application/Application.cs index b8a4e260dc89..936ce94fc74e 100644 --- a/src/Controls/src/Core/Application/Application.cs +++ b/src/Controls/src/Core/Application/Application.cs @@ -108,8 +108,8 @@ public Page? MainPage if (value is not null) { - OnParentResourcesChanged(this.GetMergedResources()); - OnParentResourcesChanged([new KeyValuePair(AppThemeBinding.AppThemeResource, _lastAppTheme)]); + value.OnResourcesChanged(this.GetMergedResources()); + value.OnResourcesChanged([new KeyValuePair(AppThemeBinding.AppThemeResource, _lastAppTheme)]); ((IElementDefinition)this).AddResourcesChangedListener(value.OnParentResourcesChanged); } diff --git a/src/Controls/src/Core/Element/Element.cs b/src/Controls/src/Core/Element/Element.cs index ca9be0ca1e16..8b8e77cf64a8 100644 --- a/src/Controls/src/Core/Element/Element.cs +++ b/src/Controls/src/Core/Element/Element.cs @@ -319,7 +319,7 @@ internal Element ParentOverride /// void IElementDefinition.AddResourcesChangedListener(Action onchanged) { - _changeHandlers = _changeHandlers ?? new List>(2); + _changeHandlers ??= new List>(2); _changeHandlers.Add(onchanged); } @@ -354,6 +354,9 @@ void SetParent(Element value) if (RealParent != null) { OnParentResourcesChanged(RealParent.GetMergedResources()); + if (Application.Current?.RequestedTheme != ApplicationModel.AppTheme.Unspecified) + OnParentResourcesChanged([new KeyValuePair(AppThemeBinding.AppThemeResource, Application.Current.RequestedTheme)]); + ((IElementDefinition)RealParent).AddResourcesChangedListener(OnParentResourcesChanged); } diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui21774.xaml b/src/Controls/tests/Xaml.UnitTests/Issues/Maui21774.xaml new file mode 100644 index 000000000000..2ac251ad3731 --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui21774.xaml @@ -0,0 +1,9 @@ + + + + + \ No newline at end of file diff --git a/src/Controls/tests/Xaml.UnitTests/Issues/Maui21774.xaml.cs b/src/Controls/tests/Xaml.UnitTests/Issues/Maui21774.xaml.cs new file mode 100644 index 000000000000..e9cdc966a46d --- /dev/null +++ b/src/Controls/tests/Xaml.UnitTests/Issues/Maui21774.xaml.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.Maui.ApplicationModel; +using Microsoft.Maui.Controls.Core.UnitTests; +using Microsoft.Maui.Controls.Shapes; +using Microsoft.Maui.Devices; +using Microsoft.Maui.Dispatching; + +using Microsoft.Maui.Graphics; +using Microsoft.Maui.UnitTests; +using NUnit.Framework; + +namespace Microsoft.Maui.Controls.Xaml.UnitTests; + +public partial class Maui21774 +{ + public Maui21774() + { + InitializeComponent(); + } + + public Maui21774(bool useCompiledXaml) + { + //this stub will be replaced at compile time + } + + [TestFixture] + class Test + { + [SetUp] + public void Setup() + { + Application.SetCurrentApplication(new MockApplication()); + DispatcherProvider.SetCurrent(new DispatcherProviderStub()); + } + + [TearDown] public void TearDown() => AppInfo.SetCurrent(null); + + [Test] + public void AppThemeChangeOnUnparentedPage([Values(false, true)] bool useCompiledXaml) + { + Application.Current.Resources.Add("labelColor", Colors.LimeGreen); + Application.Current.UserAppTheme = AppTheme.Light; + var page = new Maui21774(useCompiledXaml); + Application.Current.MainPage = page; + + Assert.That(page.label0.TextColor, Is.EqualTo(Colors.LimeGreen)); + Assert.That(page.label1.TextColor, Is.EqualTo(Colors.LimeGreen)); + + //unparent the page, change the resource and the theme + Application.Current.MainPage = null; + Application.Current.Resources["labelColor"] = Colors.HotPink; + Application.Current.UserAppTheme = AppTheme.Dark; + //labels should not change + Assert.That(page.label0.TextColor, Is.EqualTo(Colors.LimeGreen)); + Assert.That(page.label1.TextColor, Is.EqualTo(Colors.LimeGreen)); + + //reparent the page + Application.Current.MainPage = page; + //labels should change + Assert.That(page.label0.TextColor, Is.EqualTo(Colors.HotPink)); + Assert.That(page.label1.TextColor, Is.EqualTo(Colors.HotPink)); + } + } +} From 8b7c356c684a7df663be3a99412caca304dc1806 Mon Sep 17 00:00:00 2001 From: Stephane Delcroix Date: Thu, 18 Apr 2024 11:12:07 +0200 Subject: [PATCH 2/2] move logic to resourcesextensions --- src/Controls/src/Core/Application/Application.cs | 1 - src/Controls/src/Core/Element/Element.cs | 3 --- src/Controls/src/Core/ResourcesExtensions.cs | 6 ++++++ 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Controls/src/Core/Application/Application.cs b/src/Controls/src/Core/Application/Application.cs index 936ce94fc74e..404ec6f21398 100644 --- a/src/Controls/src/Core/Application/Application.cs +++ b/src/Controls/src/Core/Application/Application.cs @@ -109,7 +109,6 @@ public Page? MainPage if (value is not null) { value.OnResourcesChanged(this.GetMergedResources()); - value.OnResourcesChanged([new KeyValuePair(AppThemeBinding.AppThemeResource, _lastAppTheme)]); ((IElementDefinition)this).AddResourcesChangedListener(value.OnParentResourcesChanged); } diff --git a/src/Controls/src/Core/Element/Element.cs b/src/Controls/src/Core/Element/Element.cs index 8b8e77cf64a8..8d18b3e2458c 100644 --- a/src/Controls/src/Core/Element/Element.cs +++ b/src/Controls/src/Core/Element/Element.cs @@ -354,9 +354,6 @@ void SetParent(Element value) if (RealParent != null) { OnParentResourcesChanged(RealParent.GetMergedResources()); - if (Application.Current?.RequestedTheme != ApplicationModel.AppTheme.Unspecified) - OnParentResourcesChanged([new KeyValuePair(AppThemeBinding.AppThemeResource, Application.Current.RequestedTheme)]); - ((IElementDefinition)RealParent).AddResourcesChangedListener(OnParentResourcesChanged); } diff --git a/src/Controls/src/Core/ResourcesExtensions.cs b/src/Controls/src/Core/ResourcesExtensions.cs index ce0a2df4e90a..32673eaba4c6 100644 --- a/src/Controls/src/Core/ResourcesExtensions.cs +++ b/src/Controls/src/Core/ResourcesExtensions.cs @@ -44,6 +44,12 @@ public static IEnumerable> GetMergedResources(this resources[res.Key] = mergedClassStyles; } } + if (app != null) + { + resources = resources ?? new(StringComparer.Ordinal); + resources[AppThemeBinding.AppThemeResource] = app.RequestedTheme; + } + element = element.Parent; } return resources;