Wednesday, September 20, 2017

Is there any way to make a label fill the space from left to right?

Leave a Comment

Here is the code I have:

<StackLayout>    <Label x:Name="emptyLabel1" FontSize="18" XAlign="Start" TextColor="Gray" />    <Label x:Name="emptyLabel2" FontSize="18" XAlign="Center" TextColor="Gray" />    <Label x:Name="emptyLabel3" FontSize="18" XAlign="Center" TextColor="Gray" /> </StackLayout> 

The first multi-line label starts on the left but has spaces on some of the rows on the right. The 2nd and 3rd multi-line labels are centered and have spaces on both left and right.

Is there any way that I can have all rows of the labels completely fill the rows completely fill from left to right o that the first character of each row always lines up on the left and the last character of the last word of each row always lines up on the right? Note that this would require some words in each line to have different gaps between them.

3 Answers

Answers 1

It is a bit tricky to implement label with justify alignment support, but it is possible through platform renderer(s).

First step would be to declare a custom control in forms project.

public class JustifiedLabel : Label { } 

Next step is to define and register the platform renderer in iOS. This one is simple, as we simply combine formatted-string with paragraph-style to get what we want.

[assembly: ExportRenderer(typeof(JustifiedLabel), typeof(JustifiedLabelRenderer))] namespace SomeAppNamespace.iOS {        public class JustifiedLabelRenderer : LabelRenderer     {         protected override void OnElementChanged(ElementChangedEventArgs<Label> e)         {             base.OnElementChanged(e);              //if we have a new forms element, we want to update text with font style (as specified in forms-pcl) on native control             if (e.NewElement != null)                 UpdateTextOnControl();         }          protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)         {             base.OnElementPropertyChanged(sender, e);              //if there is change in text or font-style, trigger update to redraw control             if(e.PropertyName == nameof(Label.Text)                 || e.PropertyName == nameof(Label.FontFamily)                 || e.PropertyName == nameof(Label.FontSize)                || e.PropertyName == nameof(Label.TextColor)                || e.PropertyName == nameof(Label.FontAttributes))             {                 UpdateTextOnControl();             }         }          void UpdateTextOnControl()         {             if (Control == null)                 return;              //define paragraph-style             var style = new NSMutableParagraphStyle()             {                 Alignment = UITextAlignment.Justified,                 FirstLineHeadIndent = 0.001f,             };              //define attributes that use both paragraph-style, and font-style              var uiAttr = new UIStringAttributes()             {                 ParagraphStyle = style,                 BaselineOffset = 0,                  Font = Control.Font             };              //define frame to ensure justify alignment is applied             Control.Frame = new RectangleF(0, 0, (float)Element.Width, (float)Element.Height);              //set new text with ui-style-attributes to native control (UILabel)             var stringToJustify = Control.Text ?? string.Empty;             var attributedString = new Foundation.NSAttributedString(stringToJustify, uiAttr.Dictionary);             Control.AttributedText = attributedString;             Control.Lines = 0;         }     } } 

In android platform, it is a bit trickier - as android doesn't support justify alignment for TextView - so we will need to use a WebView instead to get it to render the text.

(Note: You can also alternatively use an android library and use it instead of WebView)

[assembly: ExportRenderer(typeof(JustifiedLabel), typeof(JustifiedLabelRenderer))] namespace SomeAppNamespace.Droid {     //We don't extend from LabelRenderer on purpose as we want to set      // our own native control (which is not TextView)     public class JustifiedLabelRenderer : ViewRenderer     {         protected override void OnElementChanged(ElementChangedEventArgs<View> e)         {             base.OnElementChanged(e);              //if we have a new forms element, we want to update text with font style (as specified in forms-pcl) on native control             if (e.NewElement != null)             {                 if (Control == null)                 {                     //register webview as native control                     var webView = new Android.Webkit.WebView(Context);                     webView.VerticalScrollBarEnabled = false;                     webView.HorizontalScrollBarEnabled = false;                      webView.LoadData("<html><body>&nbsp;</body></html>", "text/html; charset=utf-8", "utf-8");                     SetNativeControl(webView);                 }                  //if we have a new forms element, we want to update text with font style (as specified in forms-pcl) on native control                 UpdateTextOnControl();             }            }          protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)         {             base.OnElementPropertyChanged(sender, e);              //if there is change in text or font-style, trigger update to redraw control             if (e.PropertyName == nameof(Label.Text)                || e.PropertyName == nameof(Label.FontFamily)                || e.PropertyName == nameof(Label.FontSize)                || e.PropertyName == nameof(Label.TextColor)                || e.PropertyName == nameof(Label.FontAttributes))             {                 UpdateTextOnControl();             }         }          void UpdateTextOnControl()         {             var webView = Control as Android.Webkit.WebView;              var formsLabel = Element as Label;              // create css style from font-style as specified             var cssStyle = $"margin: 0px; padding: 0px; text-align: justify; color: {ToHexColor(formsLabel.TextColor)}; background-color: {ToHexColor(formsLabel.BackgroundColor)}; font-family: {formsLabel.FontFamily}; font-size: {formsLabel.FontSize}; font-weight: {formsLabel.FontAttributes}";              // apply that to text              var strData =                 $"<html><body style=\"{cssStyle}\">{formsLabel?.Text}</body></html>";              // and, refresh webview             webView.LoadData(strData, "text/html; charset=utf-8", "utf-8");             webView.Reload();         }          // helper method to convert forms-color to css-color         string ToHexColor(Color color)         {             var red = (int)(color.R * 255);             var green = (int)(color.G * 255);             var blue = (int)(color.B * 255);             var alpha = (int)(color.A * 255);             var hex = $"#{red:X2}{green:X2}{blue:X2}";              return hex;         }     } } 

Sample usage

<StackLayout Margin="20">     <Entry x:Name="InputEntry" />      <Label Margin="0,10,0,0" BackgroundColor="Navy" TextColor="White" Text="Normal Text Label" FontSize="15" HorizontalOptions="CenterAndExpand" />     <Label              FontSize="20"              FontAttributes="Bold"               Text="{Binding Text, Source={x:Reference InputEntry}}" />      <Label Margin="0,10,0,0" BackgroundColor="Navy" TextColor="White" Text="Justified Text Label" FontSize="15" HorizontalOptions="CenterAndExpand" />     <local:JustifiedLabel              FontSize="20"              FontAttributes="Bold"              Text="{Binding Text, Source={x:Reference InputEntry}}"             TextColor="Green"             BackgroundColor="Yellow"             VerticalOptions="FillAndExpand"             HorizontalOptions="FillAndExpand" />  </StackLayout> 

enter image description here enter image description here

Answers 2

I think you can try with

HorizontalOptions=LayoutOptions.FillAndExpand 

HorizontalOptions

Answers 3

I never saw any simple solution for this, only workarounds like mentioned here.

You need to use a component or create your own solution for each platform.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment