Skip to content

Commit

Permalink
Merge pull request #19104 from ramezgerges/textbox_xbind_updatesource…
Browse files Browse the repository at this point in the history
…trigger

fix(textbox): fix TextProperty change propagation in xBind
  • Loading branch information
ramezgerges authored Jan 8, 2025
2 parents 5c00751 + 55f772d commit 8d836a8
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,26 +47,30 @@ namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml_Controls
public partial class Given_TextBox
{
[TestMethod]
[DataRow(UpdateSourceTrigger.Default)]
[DataRow(UpdateSourceTrigger.PropertyChanged)]
[DataRow(UpdateSourceTrigger.Explicit)]
[DataRow(UpdateSourceTrigger.LostFocus)]
public async Task When_TwoWay_Text_Binding(UpdateSourceTrigger trigger)
[DataRow(UpdateSourceTrigger.Default, false)]
[DataRow(UpdateSourceTrigger.PropertyChanged, false)]
[DataRow(UpdateSourceTrigger.Explicit, false)]
[DataRow(UpdateSourceTrigger.LostFocus, false)]
[DataRow(UpdateSourceTrigger.Default, true)]
[DataRow(UpdateSourceTrigger.LostFocus, true)]
public async Task When_TwoWay_Text_Binding(UpdateSourceTrigger trigger, bool xBind)
{
var SUT = new When_TwoWay_Text_Binding();
var tb = trigger switch
var tb = (trigger, xBind) switch
{
UpdateSourceTrigger.Default => SUT.tbTwoWay_triggerDefault,
UpdateSourceTrigger.PropertyChanged => SUT.tbTwoWay_triggerPropertyChanged,
UpdateSourceTrigger.Explicit => SUT.tbTwoWay_triggerExplicit,
UpdateSourceTrigger.LostFocus => SUT.tbTwoWay_triggerLostFocus,
(UpdateSourceTrigger.Default, false) => SUT.tbTwoWay_triggerDefault,
(UpdateSourceTrigger.PropertyChanged, false) => SUT.tbTwoWay_triggerPropertyChanged,
(UpdateSourceTrigger.Explicit, false) => SUT.tbTwoWay_triggerExplicit,
(UpdateSourceTrigger.LostFocus, false) => SUT.tbTwoWay_triggerLostFocus,
(UpdateSourceTrigger.Default, true) => SUT.tbTwoWay_triggerDefault_xBind,
(UpdateSourceTrigger.LostFocus, true) => SUT.tbTwoWay_triggerLostFocus_xBind,
_ => throw new Exception("Should not happen."),
};
var expectedSetCount = 0;

await UITestHelper.Load(SUT);

var vm = (When_TwoWay_Text_Binding.VM)tb.DataContext;
var vm = xBind ? SUT.VMForXBind : (When_TwoWay_Text_Binding.VM)tb.DataContext;

Assert.AreNotEqual(tb, FocusManager.GetFocusedElement(SUT.XamlRoot));

Expand All @@ -75,7 +79,7 @@ public async Task When_TwoWay_Text_Binding(UpdateSourceTrigger trigger)

// Change text while not focused
tb.Text = "Hello";
if (trigger != UpdateSourceTrigger.Explicit)
if (trigger is UpdateSourceTrigger.PropertyChanged || (trigger is not UpdateSourceTrigger.Explicit && !xBind))
{
expectedSetCount++;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
<TextBox x:Name="tbTwoWay_triggerLostFocus" Text="{Binding VMProperty, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" x:FieldModifier="public" />
<TextBox x:Name="tbTwoWay_triggerPropertyChanged" Text="{Binding VMProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" x:FieldModifier="public" />
<TextBox x:Name="tbTwoWay_triggerExplicit" Text="{Binding VMProperty, Mode=TwoWay, UpdateSourceTrigger=Explicit}" x:FieldModifier="public" />
<TextBox x:Name="tbTwoWay_triggerDefault_xBind" Text="{x:Bind VMForXBind.VMProperty, Mode=TwoWay, UpdateSourceTrigger=Default}" x:FieldModifier="public" />
<TextBox x:Name="tbTwoWay_triggerLostFocus_xBind" Text="{x:Bind VMForXBind.VMProperty, Mode=TwoWay, UpdateSourceTrigger=LostFocus}" x:FieldModifier="public" />
<Button x:Name="dummyButton" Content="Dummy" x:FieldModifier="public" />
</StackPanel>
</Page>
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public string VMProperty
public int SetCount { get; private set; }
}

public VM VMForXBind { get; } = new VM();

public When_TwoWay_Text_Binding()
{
this.InitializeComponent();
Expand Down
11 changes: 9 additions & 2 deletions src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,14 @@ protected virtual void OnTextChanged(DependencyPropertyChangedEventArgs e)

var focusManager = VisualTree.GetFocusManagerForElement(this);
if (focusManager?.FocusedElement != this &&
GetBindingExpression(TextProperty) is { ParentBinding.UpdateSourceTrigger: UpdateSourceTrigger.Default or UpdateSourceTrigger.LostFocus } bindingExpression)
GetBindingExpression(TextProperty) is
{
ParentBinding:
{
IsXBind: false, // NOTE: we UpdateSource in OnTextChanged only when the binding is not an x:Bind. WinUI's generated code for x:Bind contains a simple LostFocus subscription and waits for the next LostFocus even when not focused, unlike regular Bindings.
UpdateSourceTrigger: UpdateSourceTrigger.Default or UpdateSourceTrigger.LostFocus
}
} bindingExpression)
{
bindingExpression.UpdateSource(Text);
}
Expand Down Expand Up @@ -994,7 +1001,7 @@ private void OnFocusStateChanged(FocusState oldValue, FocusState newValue, bool

UpdateButtonStates();

if (oldValue == FocusState.Unfocused || newValue == FocusState.Unfocused)
if (newValue == FocusState.Unfocused)
{
_hasTextChangedThisFocusSession = false;
}
Expand Down

0 comments on commit 8d836a8

Please sign in to comment.