Skip to content

Commit

Permalink
feat: Support ThemeResource in TargetNullValue and FallbackValue
Browse files Browse the repository at this point in the history
  • Loading branch information
Youssef1313 committed May 7, 2024
1 parent 56f15f5 commit 7514964
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4048,6 +4048,22 @@ void WriteBinding(bool isTemplateBindingAttachedProperty, string? prefix = null)
}
}

foreach (var option in bindingOptions)
{
var themeResourceCandidate = option.Objects.FirstOrDefault();
if (themeResourceCandidate?.Type is { PreferredXamlNamespace: XamlConstants.PresentationXamlXmlNamespace, Name: "ThemeResource" })
{
if (option.Member.Name == "TargetNullValue" && themeResourceCandidate.Members.FirstOrDefault().Value is string targetNullValueKey)
{
writer.AppendLineIndented($".ApplyTargetNullValueThemeResource(@\"{targetNullValueKey}\", {ParseContextPropertyAccess})");
}
else if (option.Member.Name == "FallbackValue" && themeResourceCandidate.Members.FirstOrDefault().Value is string fallbackValueKey)
{
writer.AppendLineIndented($".ApplyFallbackValueThemeResource(@\"{fallbackValueKey}\", {ParseContextPropertyAccess})");
}
}
}

// xbind initialization
if (bindNode != null && !isBindingType)
{
Expand Down
23 changes: 23 additions & 0 deletions src/Uno.UI/Helpers/BindingExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.ComponentModel;
using Microsoft.UI.Xaml.Data;

namespace Uno.UI.Helpers.Xaml;

public static class BindingExtensions
{
[EditorBrowsable(EditorBrowsableState.Never)]
public static Binding ApplyTargetNullValueThemeResource(this Binding binding, string themeResourceName, object parseContext)
{
binding.TargetNullValueThemeResource = themeResourceName;
binding.ParseContext = parseContext;
return binding;
}

[EditorBrowsable(EditorBrowsableState.Never)]
public static Binding ApplyFallbackValueThemeResource(this Binding binding, string themeResourceName, object parseContext)
{
binding.FallbackValueThemeResource = themeResourceName;
binding.ParseContext = parseContext;
return binding;
}
}
6 changes: 6 additions & 0 deletions src/Uno.UI/UI/Xaml/Data/Binding.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ public object FallbackValue
}
}

internal string FallbackValueThemeResource { get; set; }

/// <summary>
/// Gets or sets a value that indicates the direction of the data flow in the binding.
/// </summary>
Expand Down Expand Up @@ -180,6 +182,10 @@ public object Source
/// <value>The target null value.</value>
public object TargetNullValue { get; set; }

internal string TargetNullValueThemeResource { get; set; }

internal object ParseContext { get; set; }

/// <summary>
/// Gets or sets a value that determines the timing of binding source updates for two-way bindings.
/// </summary>
Expand Down
22 changes: 21 additions & 1 deletion src/Uno.UI/UI/Xaml/DependencyObjectStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1289,13 +1289,31 @@ internal void UpdateResourceBindings(ResourceUpdateReason updateReason, Resource
throw new ArgumentException();
}

ResourceDictionary[]? dictionariesInScope = null;

if (updateReason == ResourceUpdateReason.ThemeResource)
{
dictionariesInScope = GetResourceDictionaries(includeAppResources: false, containingDictionary).ToArray();
for (var i = dictionariesInScope.Length - 1; i >= 0; i--)
{
ResourceResolver.PushSourceToScope(dictionariesInScope[i]);
}

_properties.UpdateBindingExpressions();

foreach (var dict in dictionariesInScope)
{
ResourceResolver.PopSourceFromScope();
}
}

if (_resourceBindings == null || !_resourceBindings.HasBindings)
{
UpdateChildResourceBindings(updateReason);
return;
}

var dictionariesInScope = GetResourceDictionaries(includeAppResources: false, containingDictionary).ToArray();
dictionariesInScope ??= GetResourceDictionaries(includeAppResources: false, containingDictionary).ToArray();

var bindings = _resourceBindings.GetAllBindings();

Expand Down Expand Up @@ -1428,6 +1446,8 @@ private void UpdateChildResourceBindings(ResourceUpdateReason updateReason)
// Call OnThemeChanged after bindings of descendants have been updated
themeChangeAware.OnThemeChanged();
}

_properties.OnThemeChanged();
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.UI.Xaml.Data;
using Uno.Collections;
using Uno.Extensions;
using Uno.Foundation.Logging;
using Uno.UI;
using Uno.UI.DataBinding;
using Microsoft.UI.Xaml.Data;

namespace Microsoft.UI.Xaml
{
Expand Down Expand Up @@ -219,5 +220,58 @@ internal BindingExpression GetBindingExpression(DependencyProperty dependencyPro

return null;
}

internal void UpdateBindingExpressions()
{
foreach (var binding in _bindings)
{
UpdateBindingPropertiesFromThemeResources(binding.ParentBinding);
}

foreach (var binding in _templateBindings)
{
UpdateBindingPropertiesFromThemeResources(binding.ParentBinding);
}
}

private static void UpdateBindingPropertiesFromThemeResources(Binding binding)
{
if (binding.TargetNullValueThemeResource is { } targetNullValueThemeResourceKey)
{
binding.TargetNullValue = (object)ResourceResolverSingleton.Instance.ResolveResourceStatic(targetNullValueThemeResourceKey, typeof(object), context: binding.ParseContext);
}

if (binding.FallbackValueThemeResource is { } fallbackValueThemeResourceKey)
{
binding.FallbackValue = (object)ResourceResolverSingleton.Instance.ResolveResourceStatic(fallbackValueThemeResourceKey, typeof(object), context: binding.ParseContext);
}
}

internal void OnThemeChanged()
{
foreach (var binding in _bindings)
{
RefreshBindingValueIfNecessary(binding);
}

foreach (var binding in _templateBindings)
{
RefreshBindingValueIfNecessary(binding);
}
}

private void RefreshBindingValueIfNecessary(BindingExpression binding)
{
if (binding.ParentBinding.TargetNullValueThemeResource is not null ||
binding.ParentBinding.FallbackValueThemeResource is not null)
{
// Note: This may refresh the binding more than really necessary.
// For example, if TargetNullValue is set to a theme resource, but the binding is not null
// In this case, a change to TargetNullValue should probably not refresh the binding.
// Another case is when the ThemeResource evaluates the same between light/dark themes.
// For now, it's not necessary.
binding.RefreshTarget();
}
}
}
}

0 comments on commit 7514964

Please sign in to comment.