diff --git a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_TextBox.cs b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_TextBox.cs
index 7dad622a507e..b769234751b8 100644
--- a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_TextBox.cs
+++ b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/Given_TextBox.cs
@@ -694,5 +694,63 @@ public async Task When_TextBox_ImeAction_Enter()
act.Should().NotThrow();
}
#endif
+
+ [TestMethod]
+#if __SKIA__
+ [Ignore("Fails on Skia")]
+#endif
+ public async Task When_TextBox_Wrap_Custom_Style()
+ {
+ var page = new TextBox_Wrapping();
+ await UITestHelper.Load(page);
+
+ page.SUT.Text = "Short";
+ await WindowHelper.WaitForIdle();
+ var height1 = page.SUT.ActualHeight;
+
+ page.SUT.Text = "This is a very very very much longer text. This TextBox should now wrap and have a larger height.";
+ await WindowHelper.WaitForIdle();
+ var height2 = page.SUT.ActualHeight;
+
+ page.SUT.Text = "Short";
+ await WindowHelper.WaitForIdle();
+ var height3 = page.SUT.ActualHeight;
+
+ Assert.AreEqual(height1, height3);
+ height2.Should().BeGreaterThan(height1);
+ }
+
+ [TestMethod]
+#if __SKIA__ || __IOS__
+ [Ignore("Fails on Skia and iOS")]
+ // On iOS, the failure is: AssertFailedException: Expected value to be greater than 1199.0, but found 1199.0.
+ // Since the number is large, it looks like the TextBox is taking the full height.
+#endif
+ public async Task When_TextBox_Wrap_Fluent()
+ {
+ var SUT = new TextBox()
+ {
+ Width = 200,
+ TextWrapping = TextWrapping.Wrap,
+ AcceptsReturn = true,
+ };
+
+ await UITestHelper.Load(SUT);
+
+ SUT.Text = "Short";
+ await WindowHelper.WaitForIdle();
+ var height1 = SUT.ActualHeight;
+
+ SUT.Text = "This is a very very very much longer text. This TextBox should now wrap and have a larger height.";
+ await WindowHelper.WaitForIdle();
+ var height2 = SUT.ActualHeight;
+
+ SUT.Text = "Short";
+ await WindowHelper.WaitForIdle();
+ var height3 = SUT.ActualHeight;
+
+ Assert.AreEqual(height1, height3);
+ height2.Should().BeGreaterThan(height1);
+ }
}
}
diff --git a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/TextBox_Wrapping.xaml b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/TextBox_Wrapping.xaml
new file mode 100644
index 000000000000..42142fc16f13
--- /dev/null
+++ b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/TextBox_Wrapping.xaml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/TextBox_Wrapping.xaml.cs b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/TextBox_Wrapping.xaml.cs
new file mode 100644
index 000000000000..9a6ee2a58ddd
--- /dev/null
+++ b/src/Uno.UI.RuntimeTests/Tests/Windows_UI_Xaml_Controls/TextBox_Wrapping.xaml.cs
@@ -0,0 +1,11 @@
+using Windows.UI.Xaml.Controls;
+
+namespace Uno.UI.RuntimeTests.Tests.Windows_UI_Xaml_Controls;
+
+public sealed partial class TextBox_Wrapping : Page
+{
+ public TextBox_Wrapping()
+ {
+ this.InitializeComponent();
+ }
+}
diff --git a/src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.Android.cs b/src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.Android.cs
index f327f5249712..f9e5328e5444 100644
--- a/src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.Android.cs
+++ b/src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.Android.cs
@@ -87,12 +87,6 @@ private protected override void OnUnloaded()
}
}
- private protected override void OnLoaded()
- {
- base.OnLoaded();
- SetupTextBoxView();
- }
-
partial void InitializePropertiesPartial()
{
OnImeOptionsChanged(ImeOptions);
diff --git a/src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.cs b/src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.cs
index bc44a19e2d0f..db702244d402 100644
--- a/src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.cs
+++ b/src/Uno.UI/UI/Xaml/Controls/TextBox/TextBox.cs
@@ -105,6 +105,46 @@ public TextBox()
SizeChanged += OnSizeChanged;
}
+ private protected override void OnLoaded()
+ {
+ base.OnLoaded();
+
+#if __ANDROID__
+ SetupTextBoxView();
+#endif
+
+ // This workaround is added in OnLoaded rather than OnApplyTemplate.
+ // Apparently, sometimes (e.g, Material style), the TextBox style setters are executed after OnApplyTemplate
+ // So, the style setters would override what the workaround does.
+ // OnLoaded appears to be executed after both OnApplyTemplate and after the style setters, making sure the values set here are not modified after.
+ if (_contentElement is ScrollViewer scrollViewer)
+ {
+#if __IOS__ || __MACOS__
+ // We disable scrolling because the inner ITextBoxView provides its own scrolling
+ scrollViewer.HorizontalScrollMode = ScrollMode.Disabled;
+ scrollViewer.VerticalScrollMode = ScrollMode.Disabled;
+ scrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
+ scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
+#else
+ // The template of TextBox contains the following:
+ /*
+ HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
+ HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
+ VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
+ VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
+ */
+ // Historically, TemplateBinding for attached DPs wasn't supported, and TextBox worked perfectly fine.
+ // When support for TemplateBinding for attached DPs was added, TextBox broke (test: TextBox_AutoGrow_Vertically_Wrapping_Test) because of
+ // change in the values of these properties. The following code serves as a workaround to set the values to what they used to be
+ // before the support for TemplateBinding for attached DPs.
+ scrollViewer.HorizontalScrollMode = ScrollMode.Enabled; // The template sets this to Auto
+ scrollViewer.VerticalScrollMode = ScrollMode.Enabled; // The template sets this to Auto
+ scrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled; // The template sets this to Hidden
+ scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto; // The template sets this to Hidden
+#endif
+ }
+ }
+
internal bool IsUserModifying => _isInputModifyingText || _isInputClearingText;
private void OnSizeChanged(object sender, SizeChangedEventArgs args)
@@ -156,33 +196,6 @@ protected override void OnApplyTemplate()
_contentElement = GetTemplateChild(TextBoxConstants.ContentElementPartName) as ContentControl;
_header = GetTemplateChild(TextBoxConstants.HeaderContentPartName) as ContentPresenter;
- if (_contentElement is ScrollViewer scrollViewer)
- {
-#if __IOS__ || __MACOS__
- // We disable scrolling because the inner ITextBoxView provides its own scrolling
- scrollViewer.HorizontalScrollMode = ScrollMode.Disabled;
- scrollViewer.VerticalScrollMode = ScrollMode.Disabled;
- scrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled;
- scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Disabled;
-#else
- // The template of TextBox contains the following:
- /*
- HorizontalScrollBarVisibility="{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}"
- HorizontalScrollMode="{TemplateBinding ScrollViewer.HorizontalScrollMode}"
- VerticalScrollBarVisibility="{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}"
- VerticalScrollMode="{TemplateBinding ScrollViewer.VerticalScrollMode}"
- */
- // Historically, TemplateBinding for attached DPs wasn't supported, and TextBox worked perfectly fine.
- // When support for TemplateBinding for attached DPs was added, TextBox broke (test: TextBox_AutoGrow_Vertically_Wrapping_Test) because of
- // change in the values of these properties. The following code serves as a workaround to set the values to what they used to be
- // before the support for TemplateBinding for attached DPs.
- scrollViewer.HorizontalScrollMode = ScrollMode.Enabled; // The template sets this to Auto
- scrollViewer.VerticalScrollMode = ScrollMode.Enabled; // The template sets this to Auto
- scrollViewer.HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled; // The template sets this to Hidden
- scrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto; // The template sets this to Hidden
-#endif
- }
-
if (GetTemplateChild(TextBoxConstants.DeleteButtonPartName) is Button button)
{
_deleteButton = new WeakReference