[UWP] Attempt to resolve entry on UWP not correctly calculating the correct height when in a scroll view (#8214)

* Core Changes

* Recreate Previous Behavior for issue page

* Remove Resharper Setting

* Fixed control value cache and updated issue instructions

* Fix Typo

* Add copy of min/max height/width to measure copy

* Update Xamarin.Forms.ControlGallery.WindowsUniversal/CustomRenderers.cs

Co-Authored-By: Gerald Versluis <github@geraldversluis.nl>

* Revert "Remove Resharper Setting"

This reverts commit 0f491e40337c01f963f26c29b6f01a304a2e10ee.

* Reset dot settings to master

Co-authored-by: Gerald Versluis <github@geraldversluis.nl>
Co-authored-by: Samantha Houts <samhouts@users.noreply.github.com>
fixes #2172
fixes #5711
This commit is contained in:
Brian Macomber 2020-01-10 19:02:39 -06:00 коммит произвёл Samantha Houts
Родитель 0faabe0d2c
Коммит b5b99b672e
7 изменённых файлов: 274 добавлений и 63 удалений

Просмотреть файл

@ -1,5 +1,7 @@
using System.ComponentModel;
using System;
using System.ComponentModel;
using System.Linq;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
@ -12,6 +14,8 @@ using Xamarin.Forms.Platform.UWP;
[assembly: ExportRenderer(typeof(Issue1683.EntryKeyboardFlags), typeof(EntryRendererKeyboardFlags))]
[assembly: ExportRenderer(typeof(Issue1683.EditorKeyboardFlags), typeof(EditorRendererKeyboardFlags))]
[assembly: ExportRenderer(typeof(Issue3273.SortableListView), typeof(SortableListViewRenderer))]
[assembly: ExportRenderer(typeof(Issue2172OldEntry), typeof(Issue2172OldEntryRenderer))]
[assembly: ExportRenderer(typeof(Issue2172OldEditor), typeof(Issue2172OldEditorRenderer))]
namespace Xamarin.Forms.ControlGallery.WindowsUniversal
{
public class EntryRendererKeyboardFlags : EntryRenderer
@ -163,4 +167,107 @@ namespace Xamarin.Forms.ControlGallery.WindowsUniversal
m_Canvas.Children.Add(text);
}
}
public class Issue2172OldEntryRenderer : EntryRenderer
{
public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
{
if (Children.Count == 0 || Control == null)
return new SizeRequest();
var constraint = new Windows.Foundation.Size(widthConstraint, heightConstraint);
FormsTextBox child = Control;
child.Measure(constraint);
var result = new Size(Math.Ceiling(child.DesiredSize.Width), Math.Ceiling(child.DesiredSize.Height));
return new SizeRequest(result);
}
}
public class Issue2172OldEditorRenderer : EditorRenderer
{
static FormsTextBox _copyOfTextBox;
static Windows.Foundation.Size _zeroSize = new Windows.Foundation.Size(0, 0);
FormsTextBox CreateTextBox()
{
return new FormsTextBox
{
AcceptsReturn = true,
TextWrapping = TextWrapping.Wrap,
Style = Windows.UI.Xaml.Application.Current.Resources["FormsTextBoxStyle"] as Windows.UI.Xaml.Style
};
}
/*
* Purely invalidating the layout as text is added to the TextBox will not cause it to expand.
* If the TextBox is set to WordWrap and it is part of the layout it will refuse to Measure itself beyond its established width.
* Even giving it infinite constraints will cause it to always set its DesiredSize to the same width but with a vertical growth.
* The only way I was able to grow it was by setting layout renderers width explicitly to some value but then it just set its own Width to that Width which is not helpful.
* Even vertically it would measure oddly in cases of rapid text changes.
* Holding down the backspace key or enter key would cause the final result to be not quite right.
* Both of these issues were fixed by just creating a static TextBox that is not part of the layout which let me just measure
* the size of the text as it would fit into the TextBox unconstrained and then just return that Size from the GetDesiredSize call.
* */
Size GetCopyOfSize(FormsTextBox control, Windows.Foundation.Size constraint)
{
if (_copyOfTextBox == null)
{
_copyOfTextBox = CreateTextBox();
// This causes the copy to be initially setup correctly.
// I found that if the first measure of this copy occurs with Text then it will just keep defaulting to a measure with no text.
_copyOfTextBox.Measure(_zeroSize);
}
_copyOfTextBox.Text = control.Text;
_copyOfTextBox.FontSize = control.FontSize;
_copyOfTextBox.FontFamily = control.FontFamily;
_copyOfTextBox.FontStretch = control.FontStretch;
_copyOfTextBox.FontStyle = control.FontStyle;
_copyOfTextBox.FontWeight = control.FontWeight;
_copyOfTextBox.Margin = control.Margin;
_copyOfTextBox.Padding = control.Padding;
// have to reset the measure to zero before it will re-measure itself
_copyOfTextBox.Measure(_zeroSize);
_copyOfTextBox.Measure(constraint);
Size result = new Size
(
Math.Ceiling(_copyOfTextBox.DesiredSize.Width),
Math.Ceiling(_copyOfTextBox.DesiredSize.Height)
);
return result;
}
SizeRequest CalculateDesiredSizes(FormsTextBox control, Windows.Foundation.Size constraint, EditorAutoSizeOption sizeOption)
{
if (sizeOption == EditorAutoSizeOption.TextChanges)
{
Size result = GetCopyOfSize(control, constraint);
control.Measure(constraint);
return new SizeRequest(result);
}
else
{
control.Measure(constraint);
Size result = new Size(Math.Ceiling(control.DesiredSize.Width), Math.Ceiling(control.DesiredSize.Height));
return new SizeRequest(result);
}
}
public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
{
FormsTextBox child = Control;
if (Children.Count == 0 || child == null)
return new SizeRequest();
return CalculateDesiredSizes(child, new Windows.Foundation.Size(widthConstraint, heightConstraint), Element.AutoSize);
}
}
}

Просмотреть файл

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
xmlns:controls="clr-namespace:Xamarin.Forms.Controls"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:issues="clr-namespace:Xamarin.Forms.Controls.Issues"
mc:Ignorable="d"
x:Class="Xamarin.Forms.Controls.Issues.Issue2172">
<ContentPage.Content>
<StackLayout>
<Label FontSize="25" Text="Using new measure code, no text should be clipped"></Label>
<ScrollView VerticalScrollBarVisibility="Always">
<Grid BackgroundColor="Green" Padding="5">
<StackLayout>
<StackLayout>
<Entry FontSize="30" Text="{Binding Number}" />
<Entry FontSize="25" Text="Nested" />
<Entry FontSize="25" />
<Label FontSize="25" Text="Label"/>
<Editor FontSize="25" Text="Editor"></Editor>
<Editor FontSize="25" Text="Auto Size Editor, add more lines to test." AutoSize="TextChanges"></Editor>
</StackLayout>
</StackLayout>
</Grid>
</ScrollView>
<BoxView HeightRequest="10" Color="Black"></BoxView>
<Label FontSize="25" Text="Using old measure code, some text should be clipped. Don't resize the window."></Label>
<ScrollView VerticalScrollBarVisibility="Always">
<Grid BackgroundColor="Red" Padding="5">
<StackLayout>
<StackLayout>
<issues:Issue2172OldEntry FontSize="30" Text="{Binding Number}" />
<issues:Issue2172OldEntry FontSize="25" Text="Nested" />
<issues:Issue2172OldEntry FontSize="25" />
<Label FontSize="25" Text="Label"/>
<issues:Issue2172OldEditor FontSize="25" Text="Editor"></issues:Issue2172OldEditor>
<issues:Issue2172OldEditor FontSize="25" Text="Auto Size Editor, add more lines to test." AutoSize="TextChanges"></issues:Issue2172OldEditor>
</StackLayout>
</StackLayout>
</Grid>
</ScrollView>
</StackLayout>
</ContentPage.Content>
</ContentPage>

Просмотреть файл

@ -0,0 +1,33 @@
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;
namespace Xamarin.Forms.Controls.Issues
{
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 2172, "Height of Entry with data binding incorrect on UWP when Entry in ScrollView in Grid", PlatformAffected.UWP)]
public partial class Issue2172 : ContentPage
{
public Issue2172()
{
#if APP
InitializeComponent();
#endif
BindingContext = new Issue2172ViewModel();
}
public class Issue2172ViewModel
{
public string Number => "Bound Text";
}
}
public class Issue2172OldEntry : Entry
{
}
public class Issue2172OldEditor : Editor
{
}
}

Просмотреть файл

@ -36,6 +36,10 @@
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Issue7773.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue8186.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue2172.xaml.cs">
<DependentUpon>Issue2172.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)Issue3475.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue5749.xaml.cs">
<SubType>Code</SubType>
@ -1763,4 +1767,10 @@
<Generator>MSBuild:Compile</Generator>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue2172.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</EmbeddedResource>
</ItemGroup>
</Project>

Просмотреть файл

@ -12,8 +12,6 @@ namespace Xamarin.Forms.Platform.UWP
{
public class EditorRenderer : ViewRenderer<Editor, FormsTextBox>
{
private static FormsTextBox _copyOfTextBox;
static Windows.Foundation.Size _zeroSize = new Windows.Foundation.Size(0, 0);
bool _fontApplied;
Brush _backgroundColorFocusedDefaultBrush;
Brush _textDefaultBrush;
@ -179,65 +177,6 @@ namespace Xamarin.Forms.Platform.UWP
Element.SetValueCore(Editor.TextProperty, Control.Text);
}
/*
* Purely invalidating the layout as text is added to the TextBox will not cause it to expand.
* If the TextBox is set to WordWrap and it is part of the layout it will refuse to Measure itself beyond its established width.
* Even giving it infinite constraints will cause it to always set its DesiredSize to the same width but with a vertical growth.
* The only way I was able to grow it was by setting layout renderers width explicitly to some value but then it just set its own Width to that Width which is not helpful.
* Even vertically it would measure oddly in cases of rapid text changes.
* Holding down the backspace key or enter key would cause the final result to be not quite right.
* Both of these issues were fixed by just creating a static TextBox that is not part of the layout which let me just measure
* the size of the text as it would fit into the TextBox unconstrained and then just return that Size from the GetDesiredSize call.
* */
Size GetCopyOfSize(FormsTextBox control, Windows.Foundation.Size constraint)
{
if (_copyOfTextBox == null)
{
_copyOfTextBox = CreateTextBox();
// This causes the copy to be initially setup correctly.
// I found that if the first measure of this copy occurs with Text then it will just keep defaulting to a measure with no text.
_copyOfTextBox.Measure(_zeroSize);
}
_copyOfTextBox.Text = control.Text;
_copyOfTextBox.FontSize = control.FontSize;
_copyOfTextBox.FontFamily = control.FontFamily;
_copyOfTextBox.FontStretch = control.FontStretch;
_copyOfTextBox.FontStyle = control.FontStyle;
_copyOfTextBox.FontWeight = control.FontWeight;
_copyOfTextBox.Margin = control.Margin;
_copyOfTextBox.Padding = control.Padding;
// have to reset the measure to zero before it will re-measure itself
_copyOfTextBox.Measure(_zeroSize);
_copyOfTextBox.Measure(constraint);
Size result = new Size
(
Math.Ceiling(_copyOfTextBox.DesiredSize.Width),
Math.Ceiling(_copyOfTextBox.DesiredSize.Height)
);
return result;
}
SizeRequest CalculateDesiredSizes(FormsTextBox control, Windows.Foundation.Size constraint, EditorAutoSizeOption sizeOption)
{
if (sizeOption == EditorAutoSizeOption.TextChanges)
{
Size result = GetCopyOfSize(control, constraint);
control.Measure(constraint);
return new SizeRequest(result);
}
else
{
control.Measure(constraint);
Size result = new Size(Math.Ceiling(control.DesiredSize.Width), Math.Ceiling(control.DesiredSize.Height));
return new SizeRequest(result);
}
}
public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
{
@ -246,7 +185,10 @@ namespace Xamarin.Forms.Platform.UWP
if (Children.Count == 0 || child == null)
return new SizeRequest();
return CalculateDesiredSizes(child, new Windows.Foundation.Size(widthConstraint, heightConstraint), Element.AutoSize);
var constraint = new Windows.Foundation.Size(widthConstraint, heightConstraint);
child.Measure(constraint);
var result = FormsTextBox.GetCopyOfSize(child, constraint);
return new SizeRequest(result);
}
void UpdateFont()

Просмотреть файл

@ -457,5 +457,18 @@ namespace Xamarin.Forms.Platform.UWP
{
Control.IsReadOnly = Element.IsReadOnly;
}
public override SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
{
FormsTextBox child = Control;
if (Children.Count == 0 || child == null)
return new SizeRequest();
var constraint = new Windows.Foundation.Size(widthConstraint, heightConstraint);
child.Measure(constraint);
var result = FormsTextBox.GetCopyOfSize(child, constraint);
return new SizeRequest(result);
}
}
}

Просмотреть файл

@ -403,5 +403,62 @@ namespace Xamarin.Forms.Platform.UWP
WVisualStateManager.GoToState(this, state, true);
}
}
/*
* This was originally in the EditorRenderer, moved here to be shared with the entry renderer.
* It also needs to always be applied to the size calculation, not just when the box size could change.
*
* Purely invalidating the layout as text is added to the TextBox will not cause it to expand.
* If the TextBox is set to WordWrap and it is part of the layout it will refuse to Measure itself beyond its established width.
* Even giving it infinite constraints will cause it to always set its DesiredSize to the same width but with a vertical growth.
* The only way I was able to grow it was by setting layout renderers width explicitly to some value but then it just set its own Width to that Width which is not helpful.
* Even vertically it would measure oddly in cases of rapid text changes.
* Holding down the backspace key or enter key would cause the final result to be not quite right.
* Both of these issues were fixed by just creating a static TextBox that is not part of the layout which let me just measure
* the size of the text as it would fit into the TextBox unconstrained and then just return that Size from the GetDesiredSize call.
* */
static FormsTextBox _copyOfTextBox;
static readonly Windows.Foundation.Size _zeroSize = new Windows.Foundation.Size(0, 0);
public static Size GetCopyOfSize(FormsTextBox control, Windows.Foundation.Size constraint)
{
if (_copyOfTextBox == null)
{
_copyOfTextBox = new FormsTextBox
{
Style = Windows.UI.Xaml.Application.Current.Resources["FormsTextBoxStyle"] as Windows.UI.Xaml.Style
};
// This causes the copy to be initially setup correctly.
// I found that if the first measure of this copy occurs with Text then it will just keep defaulting to a measure with no text.
_copyOfTextBox.Measure(_zeroSize);
}
_copyOfTextBox.MinHeight = control.MinHeight;
_copyOfTextBox.MaxHeight = control.MaxHeight;
_copyOfTextBox.MinWidth = control.MinWidth;
_copyOfTextBox.MaxWidth = control.MaxWidth;
_copyOfTextBox.TextWrapping = control.TextWrapping;
_copyOfTextBox.AcceptsReturn = control.AcceptsReturn;
_copyOfTextBox.Text = control.Text;
_copyOfTextBox.FontSize = control.FontSize;
_copyOfTextBox.FontFamily = control.FontFamily;
_copyOfTextBox.FontStretch = control.FontStretch;
_copyOfTextBox.FontStyle = control.FontStyle;
_copyOfTextBox.FontWeight = control.FontWeight;
_copyOfTextBox.Margin = control.Margin;
_copyOfTextBox.Padding = control.Padding;
// have to reset the measure to zero before it will re-measure itself
_copyOfTextBox.Measure(_zeroSize);
_copyOfTextBox.Measure(constraint);
var result = new Size
(
Math.Ceiling(_copyOfTextBox.DesiredSize.Width),
Math.Ceiling(_copyOfTextBox.DesiredSize.Height)
);
return result;
}
}
}