diff --git a/src/SamplesApp/SamplesApp.UnitTests.Shared/Controls/UITests/Presentation/SampleChooserViewMode.Properties.cs b/src/SamplesApp/SamplesApp.UnitTests.Shared/Controls/UITests/Presentation/SampleChooserViewMode.Properties.cs index 976e73dc5d84..9298991685fd 100644 --- a/src/SamplesApp/SamplesApp.UnitTests.Shared/Controls/UITests/Presentation/SampleChooserViewMode.Properties.cs +++ b/src/SamplesApp/SamplesApp.UnitTests.Shared/Controls/UITests/Presentation/SampleChooserViewMode.Properties.cs @@ -414,9 +414,10 @@ public bool UseFluentStyles } #if HAS_UNO // Force the in app styles to reload - Application.Current.Resources?.UpdateThemeBindings(); - Uno.UI.ResourceResolver.UpdateSystemThemeBindings(); - Application.PropagateThemeChanged(Windows.UI.Xaml.Window.Current.Content); + var updateReason = ResourceUpdateReason.ThemeResource; + Application.Current.Resources?.UpdateThemeBindings(updateReason); + Uno.UI.ResourceResolver.UpdateSystemThemeBindings(updateReason); + Application.PropagateResourcesChanged(Windows.UI.Xaml.Window.Current.Content, updateReason); #endif RaisePropertyChanged(); } diff --git a/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlFileGenerator.cs b/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlFileGenerator.cs index 389a4c345ddc..0cea1189d90e 100644 --- a/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlFileGenerator.cs +++ b/src/SourceGenerators/Uno.UI.SourceGenerators/XamlGenerator/XamlFileGenerator.cs @@ -186,6 +186,8 @@ internal partial class XamlFileGenerator /// private string SingletonClassName => $"ResourceDictionarySingleton__{_fileDefinition.ShortId}"; + private bool IsHotReloadEnabled => _isDebug; + private const string DictionaryProviderInterfaceName = "global::Uno.UI.IXamlResourceDictionaryProvider"; static XamlFileGenerator() @@ -1669,10 +1671,11 @@ private void BuildPropertySetter(IIndentedStringBuilder writer, string fullTarge ParseContextPropertyAccess, propertyType ); - - if (valueObject.Type.Name == "ThemeResource") + var isThemeResource = valueObject?.Type.Name == "ThemeResource"; + var isStaticResourceForHotReload = IsHotReloadEnabled && valueObject?.Type.Name == "StaticResource"; + if (valueObject != null && (isThemeResource || isStaticResourceForHotReload)) { - writer.AppendLineInvariant(".ApplyThemeResourceUpdateValues(\"{0}\", {1})", valueObject.Members.FirstOrDefault()?.Value, ParseContextPropertyAccess); + writer.AppendLineInvariant(".ApplyThemeResourceUpdateValues(\"{0}\", {1}, {2}, {3})", valueObject.Members.FirstOrDefault()?.Value, ParseContextPropertyAccess, isThemeResource ? "true" : "false", IsHotReloadEnabled ? "true" : "false"); } writer.AppendLineInvariant(lineEnding); @@ -1734,9 +1737,11 @@ private void BuildPropertySetter(IIndentedStringBuilder writer, string fullTarge currentResourceOwner?.Dispose(); } - if (valueObject.Type.Name == "ThemeResource") + var isThemeResource = valueObject?.Type.Name == "ThemeResource"; + var isStaticResourceForHotReload = IsHotReloadEnabled && valueObject?.Type.Name == "StaticResource"; + if (valueObject != null && (isThemeResource || isStaticResourceForHotReload)) { - writer.AppendLineInvariant(".ApplyThemeResourceUpdateValues(\"{0}\", {1})", valueObject.Members.FirstOrDefault()?.Value, ParseContextPropertyAccess); + writer.AppendLineInvariant(".ApplyThemeResourceUpdateValues(\"{0}\", {1}, {2}, {3})", valueObject.Members.FirstOrDefault()?.Value, ParseContextPropertyAccess, isThemeResource ? "true" : "false", IsHotReloadEnabled ? "true" : "false"); } writer.AppendLineInvariant(lineEnding); @@ -3763,7 +3768,7 @@ private void BuildComplexPropertyValue(IIndentedStringBuilder writer, XamlMember if (IsDependencyProperty(member.Member)) { var propertyOwner = GetType(member.Member.DeclaringType); - writer.AppendLineInvariant("global::Uno.UI.ResourceResolverSingleton.Instance.ApplyResource({0}, {1}.{2}Property, \"{3}\", isThemeResourceExtension: {4}, context: {5});", closureName, GetGlobalizedTypeName(propertyOwner.ToDisplayString()), member.Member.Name, resourceKey, isThemeResourceExtension ? "true" : "false", ParseContextPropertyAccess); + writer.AppendLineInvariant("global::Uno.UI.ResourceResolverSingleton.Instance.ApplyResource({0}, {1}.{2}Property, \"{3}\", isThemeResourceExtension: {4}, isHotReloadSupported: {5}, context: {6});", closureName, GetGlobalizedTypeName(propertyOwner.ToDisplayString()), member.Member.Name, resourceKey, isThemeResourceExtension ? "true" : "false", IsHotReloadEnabled ? "true" : "false", ParseContextPropertyAccess); } else if (IsAttachedProperty(member)) { @@ -5303,9 +5308,11 @@ private void BuildChild(IIndentedStringBuilder writer, XamlMemberDefinition? own // Set ThemeResource information, if any, on Setter var valueObject = valueNode.Objects.FirstOrDefault(); - if (valueObject?.Type.Name == "ThemeResource") + var isThemeResource = valueObject?.Type.Name == "ThemeResource"; + var isStaticResourceForHotReload = IsHotReloadEnabled && valueObject?.Type.Name == "StaticResource"; + if (valueObject != null && (isThemeResource || isStaticResourceForHotReload)) { - writer.AppendLineInvariant(".ApplyThemeResourceUpdateValues(\"{0}\", {1})", valueObject.Members.FirstOrDefault()?.Value, ParseContextPropertyAccess); + writer.AppendLineInvariant(".ApplyThemeResourceUpdateValues(\"{0}\", {1}, {2}, {3})", valueObject.Members.FirstOrDefault()?.Value, ParseContextPropertyAccess, isThemeResource ? "true" : "false", IsHotReloadEnabled ? "true" : "false"); } } else if (fullTypeName == XamlConstants.Types.ResourceDictionary) diff --git a/src/Uno.UI/Helpers/Xaml/SetterExtensions.cs b/src/Uno.UI/Helpers/Xaml/SetterExtensions.cs index 54bac0286f15..c809b16f8efe 100644 --- a/src/Uno.UI/Helpers/Xaml/SetterExtensions.cs +++ b/src/Uno.UI/Helpers/Xaml/SetterExtensions.cs @@ -7,6 +7,7 @@ using Uno.Extensions; using Uno.UI.Xaml; using Windows.UI.Xaml; +using Windows.UI.Xaml.Data; namespace Uno.UI.Helpers.Xaml { @@ -14,12 +15,27 @@ public static class SetterExtensions { [EditorBrowsable(EditorBrowsableState.Never)] [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - // This is normally called from code generated by the Xaml parser + // This is normally called from code generated by the Xaml parser. This overload is kept for backwards compatibility. public static Setter ApplyThemeResourceUpdateValues(this Setter setter, string themeResourceName, object parseContext) + => ApplyThemeResourceUpdateValues(setter, themeResourceName, parseContext, isTheme: true, isHotReload: false); + + [EditorBrowsable(EditorBrowsableState.Never)] + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + // This is normally called from code generated by the Xaml parser + public static Setter ApplyThemeResourceUpdateValues(this Setter setter, string themeResourceName, object parseContext, bool isTheme, bool isHotReload) { setter.ThemeResourceKey = !themeResourceName.IsNullOrEmpty() ? themeResourceName : null; setter.ThemeResourceContext = parseContext as XamlParseContext; + if (isTheme) + { + setter.ResourceBindingUpdateReason |= ResourceUpdateReason.ThemeResource; + } + if (isHotReload) + { + setter.ResourceBindingUpdateReason |= ResourceUpdateReason.HotReload; + } + return setter; } } diff --git a/src/Uno.UI/UI/Xaml/Application.cs b/src/Uno.UI/UI/Xaml/Application.cs index 337b5a50f85a..b7850f08e872 100644 --- a/src/Uno.UI/UI/Xaml/Application.cs +++ b/src/Uno.UI/UI/Xaml/Application.cs @@ -36,6 +36,7 @@ #else using View = Windows.UI.Xaml.UIElement; using ViewGroup = Windows.UI.Xaml.UIElement; +using Windows.UI.Xaml.Data; #endif namespace Windows.UI.Xaml @@ -322,19 +323,21 @@ private void SetRequestedTheme(ApplicationTheme requestedTheme) } } - internal void UpdateResourceBindingsForHotReload() => OnRequestedThemeChanged(); + internal void UpdateResourceBindingsForHotReload() => OnResourcesChanged(ResourceUpdateReason.HotReload); - private void OnRequestedThemeChanged() + private void OnRequestedThemeChanged() => OnResourcesChanged(ResourceUpdateReason.ThemeResource); + + private void OnResourcesChanged(ResourceUpdateReason updateReason) { if (GetTreeRoot() is { } root) { // Update theme bindings in application resources - Resources?.UpdateThemeBindings(); + Resources?.UpdateThemeBindings(updateReason); // Update theme bindings in system resources - ResourceResolver.UpdateSystemThemeBindings(); + ResourceResolver.UpdateSystemThemeBindings(updateReason); - PropagateThemeChanged(root); + PropagateResourcesChanged(root, updateReason); } // Start from the real root, which may not be a FrameworkElement on some platforms @@ -354,13 +357,13 @@ View GetTreeRoot() /// /// Propagate theme changed to and its descendants, to have them update any theme bindings. /// - internal static void PropagateThemeChanged(object instance) + internal static void PropagateResourcesChanged(object instance, ResourceUpdateReason updateReason) { // Update ThemeResource references that have changed if (instance is FrameworkElement fe) { - fe.UpdateThemeBindings(); + fe.UpdateThemeBindings(updateReason); } //Try Panel.Children before ViewGroup.GetChildren - this results in fewer allocations @@ -368,14 +371,14 @@ internal static void PropagateThemeChanged(object instance) { foreach (object o in p.Children) { - PropagateThemeChanged(o); + PropagateResourcesChanged(o, updateReason); } } else if (instance is ViewGroup g) { foreach (object o in g.GetChildren()) { - PropagateThemeChanged(o); + PropagateResourcesChanged(o, updateReason); } } } diff --git a/src/Uno.UI/UI/Xaml/Controls/ComboBox/ComboBox.cs b/src/Uno.UI/UI/Xaml/Controls/ComboBox/ComboBox.cs index bc1bae0aace8..d19c71faf13a 100644 --- a/src/Uno.UI/UI/Xaml/Controls/ComboBox/ComboBox.cs +++ b/src/Uno.UI/UI/Xaml/Controls/ComboBox/ComboBox.cs @@ -65,7 +65,7 @@ public partial class ComboBox : Selector public ComboBox() { - ResourceResolver.ApplyResource(this, LightDismissOverlayBackgroundProperty, "ComboBoxLightDismissOverlayBackground", isThemeResourceExtension: true); + ResourceResolver.ApplyResource(this, LightDismissOverlayBackgroundProperty, "ComboBoxLightDismissOverlayBackground", isThemeResourceExtension: true, isHotReloadSupported: true); DefaultStyleKey = typeof(ComboBox); } diff --git a/src/Uno.UI/UI/Xaml/Controls/ContentDialog/ContentDialog.cs b/src/Uno.UI/UI/Xaml/Controls/ContentDialog/ContentDialog.cs index 5dcc64a86a4e..11d1899b82ee 100644 --- a/src/Uno.UI/UI/Xaml/Controls/ContentDialog/ContentDialog.cs +++ b/src/Uno.UI/UI/Xaml/Controls/ContentDialog/ContentDialog.cs @@ -48,7 +48,7 @@ public ContentDialog() : base() LightDismissOverlayMode = LightDismissOverlayMode.On, }; - ResourceResolver.ApplyResource(_popup, Popup.LightDismissOverlayBackgroundProperty, "ContentDialogLightDismissOverlayBackground", isThemeResourceExtension: true); + ResourceResolver.ApplyResource(_popup, Popup.LightDismissOverlayBackgroundProperty, "ContentDialogLightDismissOverlayBackground", isThemeResourceExtension: true, isHotReloadSupported: true); _popup.PopupPanel = new ContentDialogPopupPanel(this); diff --git a/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.cs b/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.cs index 4b18dbc58241..18f9d98c9113 100644 --- a/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.cs +++ b/src/Uno.UI/UI/Xaml/Controls/ContentPresenter/ContentPresenter.cs @@ -1033,9 +1033,9 @@ protected override void OnBackgroundChanged(DependencyPropertyChangedEventArgs e UpdateBorder(); } - internal override void UpdateThemeBindings() + internal override void UpdateThemeBindings(ResourceUpdateReason updateReason) { - base.UpdateThemeBindings(); + base.UpdateThemeBindings(updateReason); SetDefaultForeground(ForegroundProperty); } diff --git a/src/Uno.UI/UI/Xaml/Controls/Control/Control.cs b/src/Uno.UI/UI/Xaml/Controls/Control/Control.cs index 696477daf42a..f22b108c5ad7 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Control/Control.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Control/Control.cs @@ -60,9 +60,9 @@ private void InitializeControl() internal override bool IsEnabledOverride() => IsEnabled && base.IsEnabledOverride(); - internal override void UpdateThemeBindings() + internal override void UpdateThemeBindings(Data.ResourceUpdateReason updateReason) { - base.UpdateThemeBindings(); + base.UpdateThemeBindings(updateReason); //override the default value from dependency property based on application theme SetDefaultForeground(ForegroundProperty); diff --git a/src/Uno.UI/UI/Xaml/Controls/Flyout/FlyoutBase.cs b/src/Uno.UI/UI/Xaml/Controls/Flyout/FlyoutBase.cs index f4e849db0fe3..54f4a45566c6 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Flyout/FlyoutBase.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Flyout/FlyoutBase.cs @@ -62,7 +62,7 @@ private void EnsurePopupCreated() { if (_popup == null) { - ResourceResolver.ApplyResource(this, LightDismissOverlayBackgroundProperty, "FlyoutLightDismissOverlayBackground", isThemeResourceExtension: true); + ResourceResolver.ApplyResource(this, LightDismissOverlayBackgroundProperty, "FlyoutLightDismissOverlayBackground", isThemeResourceExtension: true, isHotReloadSupported: true); var child = CreatePresenter(); _popup = new Popup() diff --git a/src/Uno.UI/UI/Xaml/Controls/Popup/Popup.cs b/src/Uno.UI/UI/Xaml/Controls/Popup/Popup.cs index 301aa38e038b..91d6f0e2cdc0 100644 --- a/src/Uno.UI/UI/Xaml/Controls/Popup/Popup.cs +++ b/src/Uno.UI/UI/Xaml/Controls/Popup/Popup.cs @@ -65,7 +65,7 @@ private void Initialize() { InitializePartial(); - ResourceResolver.ApplyResource(this, LightDismissOverlayBackgroundProperty, "PopupLightDismissOverlayBackground", isThemeResourceExtension: true); + ResourceResolver.ApplyResource(this, LightDismissOverlayBackgroundProperty, "PopupLightDismissOverlayBackground", isThemeResourceExtension: true, isHotReloadSupported: true); ApplyLightDismissOverlayMode(); } @@ -81,7 +81,7 @@ internal PopupPanel PopupPanel set => SetValue(PopupPanelProperty, value); } - public static DependencyProperty PopupPanelProperty { get ; } = + public static DependencyProperty PopupPanelProperty { get; } = DependencyProperty.Register("PopupPanel", typeof(PopupPanel), typeof(Popup), new FrameworkPropertyMetadata(null, (s, e) => ((Popup)s)?.OnPopupPanelChanged((PopupPanel)e.OldValue, (PopupPanel)e.NewValue))); private void OnPopupPanelChanged(PopupPanel oldHost, PopupPanel newHost) @@ -154,7 +154,7 @@ partial void OnHorizontalOffsetChangedPartial(double oldHorizontalOffset, double var position = e.GetCurrentPoint(content).Position; if ( position.X < 0 || position.X > content.ActualWidth - || position.Y < 0 || position.Y > content.ActualHeight) + || position.Y < 0 || position.Y > content.ActualHeight) { popup.IsOpen = false; } @@ -173,12 +173,12 @@ partial void OnHorizontalOffsetChangedPartial(double oldHorizontalOffset, double // But this would require us to refactor more deeply the Popup which is not the purpose of the current work. }; - internal override void UpdateThemeBindings() + internal override void UpdateThemeBindings(Data.ResourceUpdateReason updateReason) { - base.UpdateThemeBindings(); + base.UpdateThemeBindings(updateReason); // Ensure bindings are updated on the child, which may be part of an isolated visual tree on some platforms (ie Android). - Application.PropagateThemeChanged(Child); + Application.PropagateResourcesChanged(Child, updateReason); } public LightDismissOverlayMode LightDismissOverlayMode @@ -235,7 +235,7 @@ internal Brush LightDismissOverlayBackground set { SetValue(LightDismissOverlayBackgroundProperty, value); } } - internal static DependencyProperty LightDismissOverlayBackgroundProperty { get ; } = + internal static DependencyProperty LightDismissOverlayBackgroundProperty { get; } = DependencyProperty.Register("LightDismissOverlayBackground", typeof(Brush), typeof(Popup), new FrameworkPropertyMetadata(defaultValue: null, propertyChangedCallback: (o, e) => ((Popup)o).ApplyLightDismissOverlayMode())); } } diff --git a/src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.cs b/src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.cs index 1bf9d1468278..103fa94da074 100644 --- a/src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.cs +++ b/src/Uno.UI/UI/Xaml/Controls/TextBlock/TextBlock.cs @@ -938,9 +938,9 @@ private void InvalidateTextBlock() private protected override double GetActualWidth() => DesiredSize.Width; private protected override double GetActualHeight() => DesiredSize.Height; - internal override void UpdateThemeBindings() + internal override void UpdateThemeBindings(Data.ResourceUpdateReason updateReason) { - base.UpdateThemeBindings(); + base.UpdateThemeBindings(updateReason); SetDefaultForeground(ForegroundProperty); } diff --git a/src/Uno.UI/UI/Xaml/Data/BindingHelper.cs b/src/Uno.UI/UI/Xaml/Data/BindingHelper.cs index 74d2678469a8..82d86addb58e 100644 --- a/src/Uno.UI/UI/Xaml/Data/BindingHelper.cs +++ b/src/Uno.UI/UI/Xaml/Data/BindingHelper.cs @@ -38,7 +38,7 @@ public static void UpdateResourceBindings(this DependencyObject instance) if(instance is IDependencyObjectStoreProvider provider) { provider.Store.ApplyElementNameBindings(); - provider.Store.UpdateResourceBindings(false); + provider.Store.UpdateResourceBindings(ResourceUpdateReason.StaticResourceLoading); } } } diff --git a/src/Uno.UI/UI/Xaml/Data/ResourceBinding.cs b/src/Uno.UI/UI/Xaml/Data/ResourceBinding.cs index 41df3bccc80e..e880b0d057fc 100644 --- a/src/Uno.UI/UI/Xaml/Data/ResourceBinding.cs +++ b/src/Uno.UI/UI/Xaml/Data/ResourceBinding.cs @@ -17,12 +17,12 @@ internal class ResourceBinding : BindingBase /// public SpecializedResourceDictionary.ResourceKey ResourceKey { get; } - /// - /// True if the original assignation used the ThemeResource extension, false if it used StaticResource. (This determines whether it - /// should be updated when the active theme changes.) - /// + public ResourceUpdateReason UpdateReason { get; } + public bool IsThemeResourceExtension { get; } + public bool IsPersistent => UpdateReason != ResourceUpdateReason.StaticResourceLoading; + public object? ParseContext { get; } public DependencyPropertyValuePrecedences Precedence { get; } @@ -32,10 +32,10 @@ internal class ResourceBinding : BindingBase /// public BindingPath? SetterBindingPath { get; } - public ResourceBinding(SpecializedResourceDictionary.ResourceKey resourceKey, bool isThemeResourceExtension, object? parseContext, DependencyPropertyValuePrecedences precedence, BindingPath? setterBindingPath) + public ResourceBinding(SpecializedResourceDictionary.ResourceKey resourceKey, ResourceUpdateReason updateReason, object? parseContext, DependencyPropertyValuePrecedences precedence, BindingPath? setterBindingPath) { ResourceKey = resourceKey; - IsThemeResourceExtension = isThemeResourceExtension; + UpdateReason = updateReason; ParseContext = parseContext; Precedence = precedence; SetterBindingPath = setterBindingPath; diff --git a/src/Uno.UI/UI/Xaml/Data/ResourceUpdateReason.cs b/src/Uno.UI/UI/Xaml/Data/ResourceUpdateReason.cs new file mode 100644 index 000000000000..5c428e70fd0c --- /dev/null +++ b/src/Uno.UI/UI/Xaml/Data/ResourceUpdateReason.cs @@ -0,0 +1,32 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Text; +using Uno.UI.DataBinding; + +namespace Windows.UI.Xaml.Data +{ + [Flags] + internal enum ResourceUpdateReason + { + None = 0, + /// + /// A static resource that could not be resolved at compile time, and should retry at loading. (This is something of a patch for compatibility gaps between Uno and WinUI) + /// + StaticResourceLoading = 1, + /// + /// An update associated with theme changing, or a resource binding that should be updated when theme changes + /// + ThemeResource = 2, + /// + /// An update associated with hot reload, or a resource binding that should be updated for hot-reload changes + /// + HotReload = 4, + + /// + /// Updates that should be propagated recursively through the visual tree + /// + PropagatesThroughTree = ThemeResource | HotReload, + } +} diff --git a/src/Uno.UI/UI/Xaml/DependencyObjectStore.Binder.cs b/src/Uno.UI/UI/Xaml/DependencyObjectStore.Binder.cs index b82d9ff9532c..cd7011929978 100644 --- a/src/Uno.UI/UI/Xaml/DependencyObjectStore.Binder.cs +++ b/src/Uno.UI/UI/Xaml/DependencyObjectStore.Binder.cs @@ -399,14 +399,14 @@ public void SetBinding(DependencyProperty dependencyProperty, BindingBase bindin } } - internal void SetResourceBinding(DependencyProperty dependencyProperty, SpecializedResourceDictionary.ResourceKey resourceKey, bool isTheme, object context, DependencyPropertyValuePrecedences? precedence, BindingPath? setterBindingPath) + internal void SetResourceBinding(DependencyProperty dependencyProperty, SpecializedResourceDictionary.ResourceKey resourceKey, ResourceUpdateReason updateReason, object context, DependencyPropertyValuePrecedences? precedence, BindingPath? setterBindingPath) { if (precedence == null && _overriddenPrecedences?.Count > 0) { precedence = _overriddenPrecedences.Peek(); } - var binding = new ResourceBinding(resourceKey, isTheme, context, precedence ?? DependencyPropertyValuePrecedences.Local, setterBindingPath); + var binding = new ResourceBinding(resourceKey, updateReason, context, precedence ?? DependencyPropertyValuePrecedences.Local, setterBindingPath); SetBinding(dependencyProperty, binding); } diff --git a/src/Uno.UI/UI/Xaml/DependencyObjectStore.cs b/src/Uno.UI/UI/Xaml/DependencyObjectStore.cs index ad8a5ebac302..908339898c2d 100644 --- a/src/Uno.UI/UI/Xaml/DependencyObjectStore.cs +++ b/src/Uno.UI/UI/Xaml/DependencyObjectStore.cs @@ -106,7 +106,7 @@ public static class TraceProvider /// /// Is a theme-bound value currently being set? /// - private bool _isSettingThemeBinding; + private bool _isSettingPersistentResourceBinding; /// /// The theme last to apply theme bindings on this object and its children. /// @@ -411,7 +411,7 @@ internal void ClearValue(DependencyProperty property, DependencyPropertyValuePre SetValue(property, DependencyProperty.UnsetValue, precedence); } - internal void SetValue(DependencyProperty property, object? value, DependencyPropertyValuePrecedences precedence, DependencyPropertyDetails? propertyDetails = null, bool isThemeBinding = false) + internal void SetValue(DependencyProperty property, object? value, DependencyPropertyValuePrecedences precedence, DependencyPropertyDetails? propertyDetails = null, bool isPersistentResourceBinding = false) { if (_trace.IsEnabled) { @@ -420,23 +420,23 @@ internal void SetValue(DependencyProperty property, object? value, DependencyPro /// can be significantly slower than other methods as a result on WebAssembly. /// See https://github.com/dotnet/runtime/issues/56309 /// - void SetValueWithTrace(DependencyProperty property, object? value, DependencyPropertyValuePrecedences precedence, DependencyPropertyDetails? propertyDetails, bool isThemeBinding) + void SetValueWithTrace(DependencyProperty property, object? value, DependencyPropertyValuePrecedences precedence, DependencyPropertyDetails? propertyDetails, bool isPersistentResourceBinding) { using (WritePropertyEventTrace(TraceProvider.SetValueStart, TraceProvider.SetValueStop, property, precedence)) { - InnerSetValue(property, value, precedence, propertyDetails, isThemeBinding); + InnerSetValue(property, value, precedence, propertyDetails, isPersistentResourceBinding); } } - SetValueWithTrace(property, value, precedence, propertyDetails, isThemeBinding); + SetValueWithTrace(property, value, precedence, propertyDetails, isPersistentResourceBinding); } else { - InnerSetValue(property, value, precedence, propertyDetails, isThemeBinding); + InnerSetValue(property, value, precedence, propertyDetails, isPersistentResourceBinding); } } - private void InnerSetValue(DependencyProperty property, object? value, DependencyPropertyValuePrecedences precedence, DependencyPropertyDetails? propertyDetails, bool isThemeBinding) + private void InnerSetValue(DependencyProperty property, object? value, DependencyPropertyValuePrecedences precedence, DependencyPropertyDetails? propertyDetails, bool isPersistentResourceBinding) { if (precedence == DependencyPropertyValuePrecedences.Coercion) { @@ -471,7 +471,7 @@ private void InnerSetValue(DependencyProperty property, object? value, Dependenc // Set even if they are different to make sure the value is now set on the right precedence SetValueInternal(value, precedence, propertyDetails); - if (!isThemeBinding && !_isSettingThemeBinding) + if (!isPersistentResourceBinding && !_isSettingPersistentResourceBinding) { // If a non-theme value is being set, clear any theme binding so it's not overwritten if the theme changes. _resourceBindings?.ClearBinding(property, precedence); @@ -1076,11 +1076,16 @@ private void CleanupInheritedProperties() /// /// Do a tree walk to find the correct values of StaticResource and ThemeResource assignations. /// - internal void UpdateResourceBindings(bool isThemeChangedUpdate, ResourceDictionary? containingDictionary = null) + internal void UpdateResourceBindings(ResourceUpdateReason updateReason, ResourceDictionary? containingDictionary = null) { + if (updateReason == ResourceUpdateReason.None) + { + throw new ArgumentException(); + } + if (_resourceBindings == null || !_resourceBindings.HasBindings) { - UpdateChildResourceBindings(isThemeChangedUpdate); + UpdateChildResourceBindings(updateReason); return; } @@ -1090,10 +1095,10 @@ internal void UpdateResourceBindings(bool isThemeChangedUpdate, ResourceDictiona foreach (var (property, binding) in bindings) { - InnerUpdateResourceBindings(isThemeChangedUpdate, dictionariesInScope, property, binding); + InnerUpdateResourceBindings(updateReason, dictionariesInScope, property, binding); } - UpdateChildResourceBindings(isThemeChangedUpdate); + UpdateChildResourceBindings(updateReason); } /// @@ -1102,11 +1107,11 @@ internal void UpdateResourceBindings(bool isThemeChangedUpdate, ResourceDictiona /// See https://github.com/dotnet/runtime/issues/56309 /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void InnerUpdateResourceBindings(bool isThemeChangedUpdate, ResourceDictionary[] dictionariesInScope, DependencyProperty property, ResourceBinding binding) + private void InnerUpdateResourceBindings(ResourceUpdateReason updateReason, ResourceDictionary[] dictionariesInScope, DependencyProperty property, ResourceBinding binding) { try { - InnerUpdateResourceBindingsUnsafe(isThemeChangedUpdate, dictionariesInScope, property, binding); + InnerUpdateResourceBindingsUnsafe(updateReason, dictionariesInScope, property, binding); } catch (Exception e) { @@ -1123,7 +1128,7 @@ private void InnerUpdateResourceBindings(bool isThemeChangedUpdate, ResourceDict /// See https://github.com/dotnet/runtime/issues/56309 /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void InnerUpdateResourceBindingsUnsafe(bool isThemeChangedUpdate, ResourceDictionary[] dictionariesInScope, DependencyProperty property, ResourceBinding binding) + private void InnerUpdateResourceBindingsUnsafe(ResourceUpdateReason updateReason, ResourceDictionary[] dictionariesInScope, DependencyProperty property, ResourceBinding binding) { var wasSet = false; foreach (var dict in dictionariesInScope) @@ -1136,7 +1141,7 @@ private void InnerUpdateResourceBindingsUnsafe(bool isThemeChangedUpdate, Resour } } - if (!wasSet && isThemeChangedUpdate && binding.IsThemeResourceExtension) + if (!wasSet && (binding.UpdateReason & updateReason) != ResourceUpdateReason.None) { if (ResourceResolver.TryTopLevelRetrieval(binding.ResourceKey, binding.ParseContext, out var value)) { @@ -1154,36 +1159,37 @@ private void SetResourceBindingValue(DependencyProperty property, ResourceBindin try #endif { - _isSettingThemeBinding = binding.IsThemeResourceExtension; + _isSettingPersistentResourceBinding = binding.IsPersistent; binding.SetterBindingPath.Value = convertedValue; } #if !HAS_EXPENSIVE_TRYFINALLY // Try/finally incurs a very large performance hit in mono-wasm - https://github.com/dotnet/runtime/issues/50783 finally #endif { - _isSettingThemeBinding = false; + _isSettingPersistentResourceBinding = false; } } else { - SetValue(property, convertedValue, binding.Precedence, isThemeBinding: binding.IsThemeResourceExtension); + SetValue(property, convertedValue, binding.Precedence, isPersistentResourceBinding: binding.IsPersistent); } } private bool _isUpdatingChildResourceBindings; - private void UpdateChildResourceBindings(bool isThemeChangedUpdate) + private void UpdateChildResourceBindings(ResourceUpdateReason updateReason) { if (_isUpdatingChildResourceBindings) { // Some DPs might be creating reference cycles, so we make sure not to enter an infinite loop. return; } - if (isThemeChangedUpdate) + + if ((updateReason & ResourceUpdateReason.PropagatesThroughTree) != ResourceUpdateReason.None) { try { - InnerUpdateChildResourceBindings(isThemeChangedUpdate); + InnerUpdateChildResourceBindings(updateReason); } finally { @@ -1204,14 +1210,14 @@ private void UpdateChildResourceBindings(bool isThemeChangedUpdate) /// See https://github.com/dotnet/runtime/issues/56309 /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void InnerUpdateChildResourceBindings(bool isThemeChangedUpdate) + private void InnerUpdateChildResourceBindings(ResourceUpdateReason updateReason) { _isUpdatingChildResourceBindings = true; foreach (var child in GetChildrenDependencyObjects()) { if (!(child is IFrameworkElement) && child is IDependencyObjectStoreProvider storeProvider) { - storeProvider.Store.UpdateResourceBindings(isThemeChangedUpdate); + storeProvider.Store.UpdateResourceBindings(updateReason); } } } @@ -1830,7 +1836,7 @@ private void CheckThemeBindings(object? previousParent, object? value) _themeLastUsed = null; if (Application.Current?.RequestedThemeForResources is { } currentTheme && !previousTheme.Equals(currentTheme)) { - Application.PropagateThemeChanged(frameworkElement); + Application.PropagateResourcesChanged(frameworkElement, ResourceUpdateReason.ThemeResource); } } } diff --git a/src/Uno.UI/UI/Xaml/FrameworkElement.cs b/src/Uno.UI/UI/Xaml/FrameworkElement.cs index 8db537c75fa0..698cd904c69a 100644 --- a/src/Uno.UI/UI/Xaml/FrameworkElement.cs +++ b/src/Uno.UI/UI/Xaml/FrameworkElement.cs @@ -23,6 +23,7 @@ using Uno.UI.DataBinding; using Uno.UI.Xaml; using Windows.UI.Xaml.Media; +using Windows.UI.Xaml.Data; #if XAMARIN_ANDROID using View = Android.Views.View; #elif XAMARIN_IOS_UNIFIED @@ -580,8 +581,8 @@ internal void EnsureFocusVisualBrushDefaults() return; } - ResourceResolver.ApplyResource(this, FocusVisualPrimaryBrushProperty, new SpecializedResourceDictionary.ResourceKey("SystemControlFocusVisualPrimaryBrush"), false, null, DependencyPropertyValuePrecedences.DefaultValue); - ResourceResolver.ApplyResource(this, FocusVisualSecondaryBrushProperty, new SpecializedResourceDictionary.ResourceKey("SystemControlFocusVisualSecondaryBrush"), false, null, DependencyPropertyValuePrecedences.DefaultValue); + ResourceResolver.ApplyResource(this, FocusVisualPrimaryBrushProperty, new SpecializedResourceDictionary.ResourceKey("SystemControlFocusVisualPrimaryBrush"), ResourceUpdateReason.ThemeResource, null, DependencyPropertyValuePrecedences.DefaultValue); + ResourceResolver.ApplyResource(this, FocusVisualSecondaryBrushProperty, new SpecializedResourceDictionary.ResourceKey("SystemControlFocusVisualSecondaryBrush"), ResourceUpdateReason.ThemeResource, null, DependencyPropertyValuePrecedences.DefaultValue); _focusVisualBrushesInitialized = true; } @@ -909,18 +910,21 @@ async void ApplyPhase() /// /// Update ThemeResource references. /// - internal virtual void UpdateThemeBindings() + internal virtual void UpdateThemeBindings(ResourceUpdateReason updateReason) { - Resources?.UpdateThemeBindings(); - (this as IDependencyObjectStoreProvider).Store.UpdateResourceBindings(isThemeChangedUpdate: true); + Resources?.UpdateThemeBindings(updateReason); + (this as IDependencyObjectStoreProvider).Store.UpdateResourceBindings(updateReason); // After theme change, the focus visual brushes may not reflect the correct settings _focusVisualBrushesInitialized = false; - // Trigger ActualThemeChanged if relevant - if (ActualThemeChanged != null && RequestedTheme == ElementTheme.Default) + if (updateReason == ResourceUpdateReason.ThemeResource) { - ActualThemeChanged?.Invoke(this, null); + // Trigger ActualThemeChanged if relevant + if (ActualThemeChanged != null && RequestedTheme == ElementTheme.Default) + { + ActualThemeChanged?.Invoke(this, null); + } } } @@ -936,7 +940,7 @@ private protected void SetDefaultForeground(DependencyProperty foregroundPropert : SolidColorBrushHelper.White, DependencyPropertyValuePrecedences.DefaultValue); } -#region AutomationPeer + #region AutomationPeer #if !__IOS__ && !__ANDROID__ && !__MACOS__ // This code is generated in FrameworkElementMixins private AutomationPeer _automationPeer; @@ -987,7 +991,7 @@ public AutomationPeer GetAutomationPeer() } #endif -#endregion + #endregion #if !UNO_REFERENCE_API private class FrameworkElementLayouter : Layouter diff --git a/src/Uno.UI/UI/Xaml/Markup/Reader/XamlObjectBuilder.cs b/src/Uno.UI/UI/Xaml/Markup/Reader/XamlObjectBuilder.cs index 4b48a9f42dec..53c46de48dd7 100644 --- a/src/Uno.UI/UI/Xaml/Markup/Reader/XamlObjectBuilder.cs +++ b/src/Uno.UI/UI/Xaml/Markup/Reader/XamlObjectBuilder.cs @@ -480,7 +480,8 @@ private void ProcessStaticResourceMarkupNode(object instance, XamlMemberDefiniti dependencyObject, dependencyProperty, keyName, - isThemeResourceExtension: IsThemeResourceMarkupNode(member)); + isThemeResourceExtension: IsThemeResourceMarkupNode(member), + isHotReloadSupported: true); if (instance is FrameworkElement fe) { diff --git a/src/Uno.UI/UI/Xaml/ResourceDictionary.cs b/src/Uno.UI/UI/Xaml/ResourceDictionary.cs index 8ca4a1cf3ceb..c3b515e49b8e 100644 --- a/src/Uno.UI/UI/Xaml/ResourceDictionary.cs +++ b/src/Uno.UI/UI/Xaml/ResourceDictionary.cs @@ -12,6 +12,7 @@ using ResourceKey = Windows.UI.Xaml.SpecializedResourceDictionary.ResourceKey; using System.Runtime.CompilerServices; +using Windows.UI.Xaml.Data; namespace Windows.UI.Xaml { @@ -541,19 +542,19 @@ public void CreationComplete() /// /// Update theme bindings on DependencyObjects in the dictionary. /// - internal void UpdateThemeBindings() + internal void UpdateThemeBindings(ResourceUpdateReason updateReason) { foreach (var item in _values.Values) { if (item is IDependencyObjectStoreProvider provider && provider.Store.Parent == null) { - provider.Store.UpdateResourceBindings(isThemeChangedUpdate: true, containingDictionary: this); + provider.Store.UpdateResourceBindings(updateReason, containingDictionary: this); } } foreach (var mergedDict in _mergedDictionaries) { - mergedDict.UpdateThemeBindings(); + mergedDict.UpdateThemeBindings(updateReason); } } diff --git a/src/Uno.UI/UI/Xaml/ResourceResolver.cs b/src/Uno.UI/UI/Xaml/ResourceResolver.cs index c50f7cdb68d6..d802f0cca74d 100644 --- a/src/Uno.UI/UI/Xaml/ResourceResolver.cs +++ b/src/Uno.UI/UI/Xaml/ResourceResolver.cs @@ -177,6 +177,10 @@ internal static object ResolveTopLevelResource(SpecializedResourceDictionary.Res return fallbackValue; } + [EditorBrowsable(EditorBrowsableState.Never)] + // This overload is kept for backwards compatibility + public static void ApplyResource(DependencyObject owner, DependencyProperty property, object resourceKey, bool isThemeResourceExtension, object context = null) + => ApplyResource(owner, property, resourceKey, isThemeResourceExtension, false, context); /// /// Apply a StaticResource or ThemeResource assignment to a DependencyProperty of a DependencyObject. The assignment will be provisionally @@ -188,24 +192,40 @@ internal static object ResolveTopLevelResource(SpecializedResourceDictionary.Res /// True for {ThemeResource Foo}, false for {StaticResource Foo} /// Optional parameter that provides parse-time context [EditorBrowsable(EditorBrowsableState.Never)] - public static void ApplyResource(DependencyObject owner, DependencyProperty property, object resourceKey, bool isThemeResourceExtension, object context = null) - => ApplyResource(owner, property, new SpecializedResourceDictionary.ResourceKey(resourceKey), isThemeResourceExtension, context, null); + public static void ApplyResource(DependencyObject owner, DependencyProperty property, object resourceKey, bool isThemeResourceExtension, bool isHotReloadSupported, object context = null) + { + var updateReason = ResourceUpdateReason.None; + if (isThemeResourceExtension) + { + updateReason |= ResourceUpdateReason.ThemeResource; + } + if (isHotReloadSupported) + { + updateReason |= ResourceUpdateReason.HotReload; + } + if (updateReason == ResourceUpdateReason.None) + { + updateReason = ResourceUpdateReason.StaticResourceLoading; + } + + ApplyResource(owner, property, new SpecializedResourceDictionary.ResourceKey(resourceKey), updateReason, context, null); + } - internal static void ApplyResource(DependencyObject owner, DependencyProperty property, SpecializedResourceDictionary.ResourceKey specializedKey, bool isThemeResourceExtension, object context, DependencyPropertyValuePrecedences? precedence) + internal static void ApplyResource(DependencyObject owner, DependencyProperty property, SpecializedResourceDictionary.ResourceKey specializedKey, ResourceUpdateReason updateReason, object context, DependencyPropertyValuePrecedences? precedence) { // Set initial value based on statically-available top-level resources. if (TryStaticRetrieval(specializedKey, context, out var value)) { owner.SetValue(property, BindingPropertyHelper.Convert(() => property.Type, value), precedence); - if (!isThemeResourceExtension) + if (updateReason == ResourceUpdateReason.StaticResourceLoading) { // If it's {StaticResource Foo} and we managed to resolve it at parse-time, then we don't want to update it again (per UWP). return; } } - (owner as IDependencyObjectStoreProvider).Store.SetResourceBinding(property, specializedKey, isThemeResourceExtension, context, precedence, null); + (owner as IDependencyObjectStoreProvider).Store.SetResourceBinding(property, specializedKey, updateReason, context, precedence, null); } /// @@ -219,7 +239,7 @@ internal static void ApplyResource(DependencyObject owner, DependencyProperty pr /// True if the value was successfully applied and registered for theme updates, false if no theme resource was found or the target /// property is not a . /// - internal static bool ApplyVisualStateSetter(SpecializedResourceDictionary.ResourceKey resourceKey, object context, BindingPath bindingPath, DependencyPropertyValuePrecedences precedence) + internal static bool ApplyVisualStateSetter(SpecializedResourceDictionary.ResourceKey resourceKey, object context, BindingPath bindingPath, DependencyPropertyValuePrecedences precedence, ResourceUpdateReason updateReason) { if (TryStaticRetrieval(resourceKey, context, out var value) && bindingPath.DataContext != null) @@ -229,7 +249,7 @@ internal static bool ApplyVisualStateSetter(SpecializedResourceDictionary.Resour { // Set current resource value bindingPath.Value = value; - provider.Store.SetResourceBinding(property, resourceKey, isTheme: true, context, precedence, bindingPath); + provider.Store.SetResourceBinding(property, resourceKey, updateReason, context, precedence, bindingPath); return true; } } @@ -483,7 +503,7 @@ public static T RetrieveCustomResource(string resourceId, string objectType, public static object ResolveStaticResourceAlias(string resourceKey, object parseContext) => ResourceDictionary.GetStaticResourceAliasPassthrough(resourceKey, parseContext as XamlParseContext); - internal static void UpdateSystemThemeBindings() => MasterDictionary.UpdateThemeBindings(); + internal static void UpdateSystemThemeBindings(ResourceUpdateReason updateReason) => MasterDictionary.UpdateThemeBindings(updateReason); } diff --git a/src/Uno.UI/UI/Xaml/ResourceResolverSingleton.cs b/src/Uno.UI/UI/Xaml/ResourceResolverSingleton.cs index 8aab9fd2f828..64fa00878988 100644 --- a/src/Uno.UI/UI/Xaml/ResourceResolverSingleton.cs +++ b/src/Uno.UI/UI/Xaml/ResourceResolverSingleton.cs @@ -34,6 +34,10 @@ public sealed class ResourceResolverSingleton public object ResolveResourceStatic(object key, Type type, object context) => ResourceResolver.ResolveResourceStatic(key, type, context); [EditorBrowsable(EditorBrowsableState.Never)] + public void ApplyResource(DependencyObject owner, DependencyProperty property, object resourceKey, bool isThemeResourceExtension, bool isHotReloadSupported, object context) => ResourceResolver.ApplyResource(owner, property, resourceKey, isThemeResourceExtension, isHotReloadSupported, context); + + [EditorBrowsable(EditorBrowsableState.Never)] + // This overload is kept for backwards compatibility public void ApplyResource(DependencyObject owner, DependencyProperty property, object resourceKey, bool isThemeResourceExtension, object context) => ResourceResolver.ApplyResource(owner, property, resourceKey, isThemeResourceExtension, context); [EditorBrowsable(EditorBrowsableState.Never)] diff --git a/src/Uno.UI/UI/Xaml/Setter.cs b/src/Uno.UI/UI/Xaml/Setter.cs index afd4a6186e95..03c0ce2e712a 100644 --- a/src/Uno.UI/UI/Xaml/Setter.cs +++ b/src/Uno.UI/UI/Xaml/Setter.cs @@ -64,6 +64,8 @@ public TargetPropertyPath? Target internal XamlParseContext? ThemeResourceContext { get; set; } + internal ResourceUpdateReason ResourceBindingUpdateReason { get; set; } + public Setter(DependencyProperty targetProperty, object value) { Property = targetProperty; @@ -96,7 +98,7 @@ internal override void ApplyTo(DependencyObject o) { if (ThemeResourceKey.HasValue) { - ResourceResolver.ApplyResource(o, Property, ThemeResourceKey.Value, isThemeResourceExtension: true, context: ThemeResourceContext, precedence: null); + ResourceResolver.ApplyResource(o, Property, ThemeResourceKey.Value, ResourceBindingUpdateReason, context: ThemeResourceContext, precedence: null); } else { @@ -116,7 +118,7 @@ internal void ApplyValue(DependencyPropertyValuePrecedences precedence, IFramewo if (path != null) { - if (ThemeResourceKey.HasValue && ResourceResolver.ApplyVisualStateSetter(ThemeResourceKey.Value, ThemeResourceContext, path, precedence)) + if (ThemeResourceKey.HasValue && ResourceResolver.ApplyVisualStateSetter(ThemeResourceKey.Value, ThemeResourceContext, path, precedence, ResourceBindingUpdateReason)) { // Applied as theme binding, no need to do more return; diff --git a/src/Uno.UI/UI/Xaml/Style/Style.cs b/src/Uno.UI/UI/Xaml/Style/Style.cs index 9c9b93f31325..73ec0bc569be 100644 --- a/src/Uno.UI/UI/Xaml/Style/Style.cs +++ b/src/Uno.UI/UI/Xaml/Style/Style.cs @@ -8,6 +8,7 @@ using Uno.Extensions; using Uno.Logging; using Uno.UI; +using Windows.UI.Xaml.Data; namespace Windows.UI.Xaml { @@ -77,7 +78,7 @@ internal void ApplyTo(DependencyObject o, DependencyPropertyValuePrecedences pre } // Check tree for resource binding values, since some Setters may have set ThemeResource-backed values - (o as IDependencyObjectStoreProvider)!.Store.UpdateResourceBindings(isThemeChangedUpdate: false); + (o as IDependencyObjectStoreProvider)!.Store.UpdateResourceBindings(ResourceUpdateReason.StaticResourceLoading); } #if !HAS_EXPENSIVE_TRYFINALLY finally diff --git a/src/Uno.UI/UI/Xaml/VisualState.cs b/src/Uno.UI/UI/Xaml/VisualState.cs index d3ae7cc42b8c..faaf3b67f4a9 100644 --- a/src/Uno.UI/UI/Xaml/VisualState.cs +++ b/src/Uno.UI/UI/Xaml/VisualState.cs @@ -6,6 +6,7 @@ using System.Text; using Uno.UI.DataBinding; using Windows.Foundation.Collections; +using Windows.UI.Xaml.Data; using Windows.UI.Xaml.Markup; using Windows.UI.Xaml.Media.Animation; @@ -170,14 +171,14 @@ private void EnsureMaterialized() if (Storyboard is IDependencyObjectStoreProvider storyboardProvider) { - storyboardProvider.Store.UpdateResourceBindings(true); + storyboardProvider.Store.UpdateResourceBindings(ResourceUpdateReason.ThemeResource); } foreach(var setter in Setters) { if (setter is IDependencyObjectStoreProvider setterProvider) { - setterProvider.Store.UpdateResourceBindings(true); + setterProvider.Store.UpdateResourceBindings(ResourceUpdateReason.ThemeResource); } } } diff --git a/src/Uno.UI/UI/Xaml/VisualTransition.cs b/src/Uno.UI/UI/Xaml/VisualTransition.cs index f71a3e3ac767..fa1e4ab950f0 100644 --- a/src/Uno.UI/UI/Xaml/VisualTransition.cs +++ b/src/Uno.UI/UI/Xaml/VisualTransition.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text; using Windows.UI.Xaml.Markup; +using Windows.UI.Xaml.Data; namespace Windows.UI.Xaml { @@ -48,7 +49,7 @@ private void EnsureMaterialized() { // Set the theme changed flag on so the update processes // the children. - storyboardProvider.Store.UpdateResourceBindings(true); + storyboardProvider.Store.UpdateResourceBindings(ResourceUpdateReason.ThemeResource); } } }