diff --git a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml/Controls/When_Refresh_Setter_BindingOnInvocation.xaml b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml/Controls/When_Refresh_Setter_BindingOnInvocation.xaml
new file mode 100644
index 000000000000..15db67c9baf0
--- /dev/null
+++ b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml/Controls/When_Refresh_Setter_BindingOnInvocation.xaml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml/Controls/When_Refresh_Setter_BindingOnInvocation.xaml.cs b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml/Controls/When_Refresh_Setter_BindingOnInvocation.xaml.cs
new file mode 100644
index 000000000000..37abf81b6df7
--- /dev/null
+++ b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml/Controls/When_Refresh_Setter_BindingOnInvocation.xaml.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.Foundation;
+using Windows.Foundation.Collections;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Controls.Primitives;
+using Windows.UI.Xaml.Data;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Navigation;
+
+// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
+
+namespace Uno.UI.RuntimeTests
+{
+ public sealed partial class When_Refresh_Setter_BindingOnInvocation : UserControl
+ {
+ public When_Refresh_Setter_BindingOnInvocation()
+ {
+ InitializeComponent();
+ }
+ }
+
+ public class When_Refresh_Setter_BindingOnInvocation_Converter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ var ctrl = value as ContentControl;
+
+ if (ctrl is not null)
+ {
+ return ctrl.Tag ?? -10;
+ }
+
+ return -10;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ throw new NotSupportedException();
+ }
+ }
+}
diff --git a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml/Controls/When_Refresh_Setter_BindingOnInvocation_ElementName.xaml b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml/Controls/When_Refresh_Setter_BindingOnInvocation_ElementName.xaml
new file mode 100644
index 000000000000..7116652ece8c
--- /dev/null
+++ b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml/Controls/When_Refresh_Setter_BindingOnInvocation_ElementName.xaml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml/Controls/When_Refresh_Setter_BindingOnInvocation_ElementName.xaml.cs b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml/Controls/When_Refresh_Setter_BindingOnInvocation_ElementName.xaml.cs
new file mode 100644
index 000000000000..090d2c0a9930
--- /dev/null
+++ b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml/Controls/When_Refresh_Setter_BindingOnInvocation_ElementName.xaml.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.InteropServices.WindowsRuntime;
+using Windows.Foundation;
+using Windows.Foundation.Collections;
+using Windows.UI.Xaml;
+using Windows.UI.Xaml.Controls;
+using Windows.UI.Xaml.Controls.Primitives;
+using Windows.UI.Xaml.Data;
+using Windows.UI.Xaml.Input;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml.Navigation;
+
+// The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236
+
+namespace Uno.UI.RuntimeTests
+{
+ public sealed partial class When_Refresh_Setter_BindingOnInvocation_ElementName : UserControl
+ {
+ public When_Refresh_Setter_BindingOnInvocation_ElementName()
+ {
+ InitializeComponent();
+ }
+ }
+
+ public class When_Refresh_Setter_BindingOnInvocation_ElementName_Converter : IValueConverter
+ {
+ public object Convert(object value, Type targetType, object parameter, string language)
+ {
+ var ctrl = value as FrameworkElement;
+
+ if (ctrl is not null)
+ {
+ return ctrl.Tag ?? -10;
+ }
+
+ return -10;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, string language)
+ {
+ throw new NotSupportedException();
+ }
+ }
+}
diff --git a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml/Given_Control.cs b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml/Given_Control.cs
index 324d9b1b7935..a9f81af50aa2 100644
--- a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml/Given_Control.cs
+++ b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml/Given_Control.cs
@@ -7,6 +7,8 @@
using Windows.UI.Xaml.Markup;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Private.Infrastructure;
+using Windows.UI.Xaml.Media;
+using Windows.UI.Xaml;
namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml
{
@@ -27,32 +29,95 @@ public async Task When_SetChildTemplateUsingVisualState()
Assert.AreEqual("Template loaded!", tb.Text);
}
- private const string _when_SetChildTemplateUsingVisualState = @"
- ";
+ private const string _when_SetChildTemplateUsingVisualState =
+ """
+
+ """;
+
+ [TestMethod]
+ [RunsOnUIThread]
+ public async Task When_Refresh_Setter_BindingOnInvocation()
+ {
+ var SUT = new When_Refresh_Setter_BindingOnInvocation();
+ TestServices.WindowHelper.WindowContent = SUT;
+ await TestServices.WindowHelper.WaitForIdle();
+
+ SUT.root.Content = 42;
+
+ var testTransform = (SUT.root.FindName("ContentElement") as FrameworkElement).RenderTransform as CompositeTransform;
+
+ Assert.IsNotNull(testTransform);
+
+ Assert.AreEqual(0, testTransform.TranslateX);
+
+ VisualStateManager.GoToState(SUT.root, "Normal", false);
+ await TestServices.WindowHelper.WaitForIdle();
+ Assert.AreEqual(-10, testTransform.TranslateY);
+
+ SUT.root.Tag = 42;
+ VisualStateManager.GoToState(SUT.root, "Focused", false);
+ await TestServices.WindowHelper.WaitForIdle();
+ Assert.AreEqual(42, testTransform.TranslateY);
+
+ SUT.root.Tag = 43;
+ VisualStateManager.GoToState(SUT.root, "Normal", false);
+ await TestServices.WindowHelper.WaitForIdle();
+ Assert.AreEqual(43, testTransform.TranslateY);
+ }
+
+ [TestMethod]
+ [RunsOnUIThread]
+ public async Task When_Refresh_Setter_BindingOnInvocation_ElementName()
+ {
+ var SUT = new When_Refresh_Setter_BindingOnInvocation_ElementName();
+ TestServices.WindowHelper.WindowContent = SUT;
+ await TestServices.WindowHelper.WaitForIdle();
+
+ SUT.root.Content = 42;
+
+ var testTransform = (SUT.root.FindName("ContentElement") as FrameworkElement).RenderTransform as CompositeTransform;
+
+ Assert.IsNotNull(testTransform);
+
+ Assert.AreEqual(0, testTransform.TranslateX);
+
+ VisualStateManager.GoToState(SUT.root, "Normal", false);
+ await TestServices.WindowHelper.WaitForIdle();
+ Assert.AreEqual(-10, testTransform.TranslateY);
+
+ SUT.sp01.Tag = 42;
+ VisualStateManager.GoToState(SUT.root, "Focused", false);
+ await TestServices.WindowHelper.WaitForIdle();
+ Assert.AreEqual(42, testTransform.TranslateY);
+
+ SUT.sp01.Tag = 43;
+ VisualStateManager.GoToState(SUT.root, "Normal", false);
+ await TestServices.WindowHelper.WaitForIdle();
+ Assert.AreEqual(43, testTransform.TranslateY);
+ }
}
}
diff --git a/src/Uno.UI/DataBinding/BindingExpression.cs b/src/Uno.UI/DataBinding/BindingExpression.cs
index 26d0ed9e6b3c..62a57d72d951 100644
--- a/src/Uno.UI/DataBinding/BindingExpression.cs
+++ b/src/Uno.UI/DataBinding/BindingExpression.cs
@@ -280,6 +280,28 @@ internal void ResumeBinding()
}
}
+ ///
+ /// Refreshes the value to the target, as the bound source may not be observable
+ ///
+ internal void RefreshTarget()
+ {
+ ApplyElementName();
+
+ if (
+ // If a listener is set, ApplyBindings has been invoked
+ _bindingPath.ValueChangedListener is not null
+
+ // If this is not an x:Bind
+ && _updateSources is null
+
+ // If there's a valid DataContext
+ && GetWeakDataContext() is { IsAlive: true } weakDataContext)
+ {
+ // Apply the source on the target again (e.g. to reevaluate converters)
+ _bindingPath.SetWeakDataContext(weakDataContext);
+ }
+ }
+
///
/// Turns UpdateSourceTrigger.Default to DependencyProperty's FrameworkPropertyMetadata.DefaultUpdateSourceTrigger
///
diff --git a/src/Uno.UI/UI/Xaml/Setter.cs b/src/Uno.UI/UI/Xaml/Setter.cs
index 39c75c02dffc..1da264fec7d5 100644
--- a/src/Uno.UI/UI/Xaml/Setter.cs
+++ b/src/Uno.UI/UI/Xaml/Setter.cs
@@ -34,6 +34,15 @@ public Setter()
private DependencyProperty? _property;
private TargetPropertyPath? _target;
+ // This property is not part of the WinUI API, but is
+ // required to determine if the value has a binding set
+ private static DependencyProperty InternalValueProperty { get; }
+ = DependencyProperty.Register(
+ nameof(Value),
+ typeof(object),
+ typeof(Setter),
+ new FrameworkPropertyMetadata(default(object)));
+
public object? Value
{
get
@@ -145,6 +154,8 @@ internal void ApplyValue(DependencyPropertyValuePrecedences precedence, IFramewo
if (path != null)
{
+ RefreshBindingPath();
+
if (ThemeResourceKey.HasValue && ResourceResolver.ApplyVisualStateSetter(ThemeResourceKey.Value, ThemeResourceContext, path, precedence, ResourceBindingUpdateReason))
{
// Applied as theme binding, no need to do more
@@ -157,6 +168,10 @@ internal void ApplyValue(DependencyPropertyValuePrecedences precedence, IFramewo
}
}
+ private void RefreshBindingPath()
+ // force binding value to re-evaluate the source and use converters
+ => GetBindingExpression(InternalValueProperty)?.RefreshTarget();
+
private BindingPath? TryGetOrCreateBindingPath(DependencyPropertyValuePrecedences precedence, IFrameworkElement owner)
{
if (_bindingPath != null)
diff --git a/src/Uno.UI/UI/Xaml/Style/mergedstyles.xaml b/src/Uno.UI/UI/Xaml/Style/mergedstyles.xaml
index 95e9a4487a70..893ae9268d49 100644
--- a/src/Uno.UI/UI/Xaml/Style/mergedstyles.xaml
+++ b/src/Uno.UI/UI/Xaml/Style/mergedstyles.xaml
@@ -1,4 +1,4 @@
-
+
diff --git a/src/Uno.UI/UI/Xaml/VisualStateGroup.cs b/src/Uno.UI/UI/Xaml/VisualStateGroup.cs
index af5cdec199c9..5f8887ffe6af 100644
--- a/src/Uno.UI/UI/Xaml/VisualStateGroup.cs
+++ b/src/Uno.UI/UI/Xaml/VisualStateGroup.cs
@@ -336,6 +336,8 @@ void ApplyTargetStateSetters()
while (settersEnumerator.MoveNext())
{
settersEnumerator.Current.ApplyValue(DependencyPropertyValuePrecedences.Animations, element);
+
+
}
}
#if !HAS_EXPENSIVE_TRYFINALLY