Bug 42559 - LineBreakMode does not work on UWP
Summary: LineBreakMode does not work on UWP
Status: RESOLVED FIXED
Alias: None
Product: Forms
Classification: Xamarin
Component: Windows (show other bugs)
Version: 2.2.0
Hardware: PC Windows
: Normal normal
Target Milestone: ---
Assignee: Bugzilla
URL:
: 41420 47708 53911 (view as bug list)
Depends on:
Blocks:
 
Reported: 2016-07-14 15:48 UTC by Ray Kelly
Modified: 2017-03-24 14:37 UTC (History)
20 users (show)

See Also:
Tags: ac
Is this bug a regression?: ---
Last known good build:


Attachments
Android on left, UWP on right (75.49 KB, image/png)
2016-07-14 15:48 UTC, Ray Kelly
Details

Description Ray Kelly 2016-07-14 15:48:06 UTC
Created attachment 16669 [details]
Android on left, UWP on right

LineBreakMode does not work on UWP.  XAML:

<Label HorizontalOptions="StartAndExpand" Text="this is a very long sentance that should be broken up into multiple lines"   FontSize="14" LineBreakMode="WordWrap"/>

Screenshot attached showing Android on left and UWP on right.
Comment 1 Paul DiPietro [MSFT] 2016-08-04 20:43:07 UTC
Do you have a fuller reproduction you could provide? I'm not able to reproduce this using a baseline line on a page (C# or XAML) and it might be because it seems like you're using your label inside a cell template or something like that.
Comment 2 Ray Kelly 2016-08-05 12:24:57 UTC
Yes, its inside of a data template and a viewcell.  Here is the XAML (look for LineBreakMode):

        <local:ServersListView.ItemTemplate>
          <DataTemplate>
            <ViewCell>
              <ViewCell.ContextActions>
                <MenuItem Clicked="OnEdit" CommandParameter="{Binding .}"
                   Text="Edit" />
                <MenuItem Clicked="OnDelete" CommandParameter="{Binding .}"
                   Text="Delete" IsDestructive="True" />
              </ViewCell.ContextActions>
                <StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand" Padding="3,0,15,10">
                    <Image x:Name="imageFaveIcon" WidthRequest="60" HeightRequest="60"  Source="{Binding ImageSource}" Margin="0,0,5,0" />
                      <StackLayout HorizontalOptions="FillAndExpand">
                        <StackLayout Orientation="Horizontal" HorizontalOptions="FillAndExpand">
                          <Label HorizontalOptions="StartAndExpand" Text="{Binding ServerName}" FontAttributes="Bold" FontSize="14" TextColor="White" />
                          <Label HorizontalOptions="EndAndExpand" Text="{Binding Info}" FontSize="14" TextColor="Yellow" Margin="0,0,0,0"/>
                        </StackLayout>
                        <StackLayout HorizontalOptions="FillAndExpand">
                          <Label HorizontalOptions="StartAndExpand" Text="{Binding Address}" FontSize="16" TextColor="White"/>
                          <Label HorizontalOptions="StartAndExpand" IsVisible="{Binding HasMOTD}" Text="" FormattedText="{Binding FormattedMOTD}" FontSize="14" LineBreakMode="WordWrap" TextColor="{Binding MOTDColor}" WidthRequest="500"/>
                        </StackLayout>
                     </StackLayout>
                </StackLayout>
            </ViewCell>
          </DataTemplate>
        </local:ServersListView.ItemTemplate>
Comment 3 Ray Kelly 2016-09-12 17:46:32 UTC
@Paul, any progress in this or a workaround.  My production app looks terrible.  Thanks.
Comment 4 Guillaume ZAHRA 2016-09-13 10:13:53 UTC
Hello Ray,

As you stated, it seem there is no fix at the moment for this case on UWP (and even WP8.1 if i'm not wrong).

I have made my own workaround, this is not something magic, and need some tweaking, but it work for my app.

Instead of using "Label" i have created a class "BigLabel" with Label inherited that look like this:

---- CODE ------

    public class BigLabel : Label
    {
        public static readonly BindableProperty TextPaddingProperty =
        BindableProperty.Create<BigLabel, double>(p => p.TextPadding, 0, BindingMode.OneWay, null,
            (bindable, oldValue, newValue) => { ((BigLabel)bindable).UpdatePadding(newValue); });

        public double TextPadding
        {
            get { return (double)GetValue(TextPaddingProperty); }
            set { SetValue(TextPaddingProperty, value); }

        }

        public BigLabel() : base()
	{
            //this.SizeChanged += (s, e) =>
            //{
            //    OnPropertyChanged();
            //};
        }

        private void UpdatePadding(double newValue)
        {
            //Managed by CustomRenderers
        }
    }

--------- /CODE ---------

Then i use a CustomRenderer on UWP, that look like this:

----- CODE -----

[assembly: ExportRenderer(typeof(BigLabel), typeof(LabelCustomRenderer))]
namespace MyCoolCompanyApp.UWP
{
    public class LabelCustomRenderer : LabelRenderer
    {
        private double FindSmallestParentContainer(VisualElement elem, double currentSize)
        {
            if (elem.Width != -1 && (elem.Width < currentSize || currentSize == -1))
                currentSize = elem.Width;
            if (elem.ParentView != null)
                return FindSmallestParentContainer(elem.ParentView, currentSize);
            return currentSize;
        }

        //protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        //{
        //    base.OnElementPropertyChanged(sender, e);

        //    if (Element.ParentView != null)
        //    {
        //        double deltaBigLabel = 0;
        //        if (Element is BigLabel)
        //            deltaBigLabel = ((BigLabel)Element).TextPadding;

        //        double size = FindSmallestParentContainer(Element.ParentView, Element.Width);
        //        Element.WidthRequest = size - Consts.BigLabelSizeDelta - deltaBigLabel;
        //    }
        //}

        /// <summary>
        /// Corrige le Non respect du LineBreak sur UWP et WP8
        /// Peut être sur téléphone ?
        /// </summary>
        /// <param name="e"></param>
        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);
            if (e.NewElement != null && e.NewElement.LineBreakMode != LineBreakMode.NoWrap)
            {
                if (e.NewElement.ParentView != null)
                {
                    double deltaBigLabel = 0;
                    if (e.NewElement is BigLabel)
                        deltaBigLabel = ((BigLabel)e.NewElement).TextPadding;

                    double size = FindSmallestParentContainer(e.NewElement.ParentView, e.NewElement.Width);
                    e.NewElement.WidthRequest = size - Consts.BigLabelSizeDelta - deltaBigLabel;
                }
            }
        }
    }
}

--------- /CODE --------

So the workaround is that the CustomRenderer will try to find the Smallest Parent Container, as the "good" computed width will be lesser than the current width (because of the bug) or different from -1 if not set.

The "TextPadding" property is used only for UWP. It just say to the renderer to Remove a specific amount of pixel from the widh. Its not something automatic, i set it approximatively, manually from the other inline content size wich shares the same parent.

For my app, it cover 90% of my cases, as its always something like an icon/image, then the text to linebreak.

I Hope it can help you...
Comment 5 Guillaume ZAHRA 2016-09-13 10:56:40 UTC
Sorry for the double post. I just forgot to say that i have sometime some issues were text is disappearing randomly in my app but i don't know if its a bug or something related to my renderer. So some tweaking may be still needed.
Comment 6 Ray Kelly 2016-09-13 23:19:51 UTC
Wow, thats quite a work around.  Yes, I can see how that would cause some display issues.  It would really affect mine since I am using "FormattedString" which shows different sizes, styles and fonts.   I think I am just going to have to wait for the real fix.   Hopefully this bug gets moving soon.
Comment 7 Guillaume ZAHRA 2016-09-14 08:56:40 UTC
Actually, the LineBreak should work even with your formatted string using my code. My code only set the "parent size width" - "TextPadding you set".

Then, your container element has a defined size instead of the undefined buggy one. So, the LineBreak will be respected with this workaround.

Of course you may wait for a fix but this bug is here since a moment...

But i just remember that i have made some recent change that may "fix" this wrong behavior on UWP.

I don't know if this is a performance killer, but can you try to embed your code in a grid with rowdefinition ? Something like this:

----------- CODE ---------------

    <StackLayout
        Style="{StaticResource MainMenuStackLayoutItemTemplateStyle_Level_2}">

        <Grid x:Name="GridSelector" HorizontalOptions="FillAndExpand">
          <Grid.RowDefinitions>
            <RowDefinition Height="*" />
          </Grid.RowDefinitions>
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="20" />
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
          </Grid.ColumnDefinitions>

          <StackLayout Grid.Row="0" Grid.Column="0"
            Orientation="Horizontal"
            VerticalOptions="Center">

            <StackLayout x:Name="LeftContentStackLayout" VerticalOptions="FillAndExpand"></StackLayout>
            
          </StackLayout>

            <StackLayout Grid.Row="0" Grid.Column="1" x:Name="MainContentStackLayout" 
              Style="{StaticResource MainMenuStackLayoutItemTemplateStyle_Level_3}"></StackLayout>

            <StackLayout Grid.Row="0" Grid.Column="2" x:Name="RightContentStackLayout" 
              Style="{StaticResource MainMenuStackLayoutItemTemplateStyle_Chevron}"></StackLayout>
        </Grid>

      </StackLayout>


--------------- /CODE ------------

The grid my workaround the undefined width problem with LineBreak, but i'm not totally sure.

Also, a new updated has been released just yesterday, i don't have tested yet if it fix the bug on UWP.
Comment 8 Andy Sheppard 2016-10-06 10:46:20 UTC
I had this issue as well while attempting to port my Xamarin.Forms app to UWP and the workaround above didn't work as various things could happen that caused my labels to resize.

I was on the verge of giving up on this when I noticed that one of my labels was wrapping as expected.  This seems to be because it was inside a ScrollView.  To fix all of my labels, I added the following code to my custom control class (NB. you'll need to extend this slightly if you have any labels that aren't in ScrollViews or StackLayouts):

----------- CODE ---------------

        protected override void OnParentSet()
        {
            base.OnParentSet();

            if (Device.OS == TargetPlatform.Windows && this.Parent is StackLayout && this.LineBreakMode != LineBreakMode.NoWrap)
            {
                Task.Run(() =>
                {
                    Device.BeginInvokeOnMainThread(() =>
                    {
                        StackLayout stackLayout = this.Parent as StackLayout;
                        int location = stackLayout.Children.IndexOf(this);

                        stackLayout.Children.Remove(this);
                        ScrollView scrollView = new ScrollView { Content = this };
                        stackLayout.Children.Insert(location, scrollView);
                    });
                });
            }
        }

--------------- /CODE ------------

The Task.Run => Device.BeginInvokeOnMainThread is just there to defer execution until after the Children collection is freed up.

All that said, I'd love to remove this code so I'm hopeful this bug will be fixed soon.
Comment 9 Christer van der Meeren 2016-10-08 16:23:40 UTC
I can confirm this bug affects me too. If I place a label in a horizontal StackLayout, it doesn't break on Windows. (I think I read somewhere that StackLayouts on Windows are infinitely large in the Orientation direction, which if true could be the root of the bug.)
Comment 10 georg.haberl 2016-10-20 09:02:29 UTC
I have the same issue, but after some trial and error I think I figured out, that the problem isn't that it doesn't wrap but the size is not properly calculated --> in my case if I have a Grid with two columns, if the left label wraps, and the right element is vertically centered, the right element will be cut off if the label on the left is high enough --> this means the grid doesn't adjust to its own contents for some reason (maybe its a timing issue where the label height due to wrapping is calculated after the grid height? ore something like that?)
Comment 11 NMackay 2016-10-20 11:03:21 UTC
This bug is affecting us too in every UWP app.

Even setting the LineBreakMode to NoWrap for UWP doesn't make a difference. It affects data template sin Listviews.

<Label Grid.Column="1" Grid.Row="0" Grid.ColumnSpan="2"
        FontAttributes="Bold" TextColor="#39757F"
        Text="{Binding VesselName}">
        <!-- BUG: Linebreakmode in UWP causes -->
        <Label.LineBreakMode>
            <OnPlatform x:TypeArguments="LineBreakMode" iOS="TailTruncation"
                        Android="TailTruncation" WinPhone="NoWrap" />
        </Label.LineBreakMode>-
Comment 12 Jeff Dalby 2016-10-23 19:08:28 UTC
I've got this but I need the opposite of most, I need it to tail truncate, which also doesn't work on UWP.  We found we can work around it by setting the Horizontal/Vertical options to Start, and giving the label a height request.
Comment 13 georg.haberl 2016-11-11 14:03:49 UTC
Can we raise the importance? Can't give publish the app without this.
Comment 14 Paul DiPietro [MSFT] 2016-11-28 04:30:36 UTC
*** Bug 47708 has been marked as a duplicate of this bug. ***
Comment 15 Samantha Houts [MSFT] 2016-12-12 22:17:13 UTC
See https://github.com/xamarin/Xamarin.Forms/pull/639
Comment 16 John Hardman 2017-01-11 09:50:14 UTC
@Samantha Houts - Mentioning this here as the generic title of this bug would encompass this case. Happy to raise a new bug specifically for this if necessary. Let me know if you'd like me to do so.

On UWP, using 2.3.3.180, Labels using CharacterWrap are actually word wrapped rather than character wrapped.

                Label longLabelWithHorizontalTextAlignmentOfEndAndCharacterWrap = new Label
                {
                    TextColor = Color.Black,
                    BackgroundColor = Color.Pink,
                    Text =
                        "This is a long string that should hopefully wrap. It has CharacterWrap enabled and HorizontalTextAlignment = End",
                    LineBreakMode = LineBreakMode.CharacterWrap,
                    HorizontalTextAlignment = TextAlignment.End
                };
Comment 17 Samantha Houts [MSFT] 2017-02-21 20:56:50 UTC
*** Bug 41420 has been marked as a duplicate of this bug. ***
Comment 18 faceoffers28 2017-02-28 20:42:25 UTC
This is a problem for me as well. The ListView text does not wrap on my Xamarin.Forms UWP app. It works just fine on iOS and Android. I am running Xamarin.Forms 2.2.0.31.

<StackLayout Grid.Row="7" Orientation="Vertical">
            <ListView x:Name="offerListView" RowHeight="50" ItemsSource="{Binding SharedOfferList}" ItemTapped="OnItemTapped">
              <ListView.ItemTemplate>
                <DataTemplate>
                  <ViewCell>
                    <ViewCell.ContextActions>
                      <!--<MenuItem Clicked="OnDelete" Text="Select" CommandParameter="{Binding Id}" IsDestructive="True" />-->
                    </ViewCell.ContextActions>
                    <ViewCell.View>
                      <ContentView BackgroundColor="#f9f9f9">

                        <StackLayout HorizontalOptions="StartAndExpand" Orientation="Horizontal">
                          <Label Text="{Binding Offer.Name}" YAlign="Center" FontSize="18" TextColor="#636E7B" />
                          <!---  <StackLayout VerticalOptions="StartAndExpand" Orientation="Vertical"> -->
                          <StackLayout HorizontalOptions="StartAndExpand" Orientation="Horizontal">
                            <Label Text="{Binding Id, Converter={StaticResource guidConverter}}" YAlign="Center" FontSize="18" TextColor="#636E7B" />
                          </StackLayout>
                        </StackLayout>

                      </ContentView>
                    </ViewCell.View>

                  </ViewCell>
                </DataTemplate>
              </ListView.ItemTemplate>
            </ListView>

          </StackLayout>
Comment 19 Rui Marinho 2017-03-14 10:59:05 UTC
Should be fixed on 2.3.5-pre1
Comment 20 Rui Marinho 2017-03-22 11:08:36 UTC
Moving this fix to 2.3.4-pre6
Comment 21 Paul DiPietro [MSFT] 2017-03-24 14:37:50 UTC
*** Bug 53911 has been marked as a duplicate of this bug. ***

Note You need to log in before you can comment on or make changes to this bug.