2

I am using a BoxView to accomplish underlining in my app. I have a couple of labels that are very short - Text such as Yes or No etc. Here is the XAML for one of the labels with the BoxView for underlining:

<StackLayout Orientation="Vertical" Grid.Row="5" Grid.Column="1" Margin="0,4,0,4" HorizontalOptions="Start" BackgroundColor="Purple" MinimumWidthRequest="1">
    <Label x:Name="txtUseMetric" TextColor="Blue" FontSize="Small" Text="{Binding UseMetricText}" BackgroundColor="Yellow">
    <Label.GestureRecognizers>
        <TapGestureRecognizer Tapped="Value_Tapped" CommandParameter="usemetric" />
    </Label.GestureRecognizers>
    </Label>
    <BoxView BackgroundColor="Green" HeightRequest="1" MinimumWidthRequest="1" />
</StackLayout>

My problem is that the width of the BoxView is always extending past my text I have tried overriding the MinWidthRequest in my App.Xaml file as seen below:

<Style TargetType="BoxView">
    <Setter Property="MinimumWidthRequest" Value="3" />
</Style>

But this has not effect. I have included screen shots for you to see.

FYI - The yellow is the width of the Label. You don't see any purple (the background color of the StackLayout) because the StackLayout and Label are the same width. The second screen shot shows what the screen looks like if I remove the BoxView - i.e. the Label and StackLayout are sized correctly.

Any suggestions on how to fix this?

Screen shot with BoxView Too Long making label and StackLayout too long Screen shot with BoxView Too Long

Screen shot with BoxView removed and Label and Stack Layout sizing correctly enter image description here

George M Ceaser Jr
  • 1,497
  • 3
  • 23
  • 52
  • Any reason you are using a box view to underline rather than adding underline capability to label? – Steve Chadbourne Dec 04 '17 at 23:06
  • You mean write my own custom renderer? – George M Ceaser Jr Dec 05 '17 at 03:08
  • Yes. Its very simple. I'll write an answer with the code. – Steve Chadbourne Dec 05 '17 at 03:10
  • I prefer not to write custom platform code. It is more susceptible to breaking and each platform changes. Plus i need to support UWP as well as android and ios. – George M Ceaser Jr Dec 05 '17 at 03:34
  • If you are working with Xamarin.Forms, as much as you may want to stay away from platform specific code (and I did initially) you will ultimately be forced to embrace it. It is the only way to push past some of the limitations imposed by Xamarin.Forms. If you try and avoid it you end up putting in more effort and code and ending up with a poorer solution. – Steve Chadbourne Dec 05 '17 at 05:01
  • Not true in my case. I have already done my project and published it with no code in the platform specific projects other than control initialization. I am simply trying to fox this defect in my app. – George M Ceaser Jr Dec 05 '17 at 11:40

2 Answers2

6

Please note the default HorizontalOptions and that Label derives from View:

Default value is LayoutOptions.Fill unless otherwise documented.

Add HorizontalOptions="Start" on the "Use Metric" Label:

<Label x:Name="txtUseMetric" TextColor="Blue" FontSize="Small" 
       Text="{Binding UseMetricText}" BackgroundColor="Yellow"
       HorizontalOptions="Start">
<BoxView BackgroundColor="Green" HeightRequest="1" 
         WidthRequest="{Binding Path=Width, Source={x:Reference txtUseMetric}" 
         HorizontalOptions="Start"/>
Benl
  • 2,777
  • 9
  • 13
  • So it appears this is the actual piece of code the fixed it. WidthRequest="{Binding Path=Width, Source={x:Reference txtUseMetric}" (I had everything as horizontaloptions="start" before and it did not work just doing that.) – George M Ceaser Jr Dec 07 '17 at 18:55
0

One option is to replace the label/box underline with a custom renderer that adds an underline capability to the label.

Here is how to do it:

User Control

public class CustomLabel : Label
{
    public static readonly BindableProperty IsUnderlinedProperty =
        BindableProperty.Create(nameof(IsUnderlined), typeof(bool), typeof(CustomLabel), false);

    public bool IsUnderlined
    {
        get { return (bool)GetValue(IsUnderlinedProperty); }
        set
        {
            SetValue(IsUnderlinedProperty, value);
        }
    }
}

Android renderer

[assembly: ExportRenderer(typeof(CustomLabel), typeof(CustomLabelRenderer))]
namespace Incident.Droid.CustomRenderers
{
    public class CustomLabelRenderer : LabelRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);

            var view = (CustomLabel)Element;
            var control = Control;

            UpdateUi(view, control);
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            var view = (CustomLabel)Element;

            if (e.PropertyName == CustomLabel.IsUnderlinedProperty.PropertyName)
            {
                Control.PaintFlags = view.IsUnderlined ? Control.PaintFlags | PaintFlags.UnderlineText : Control.PaintFlags &= ~PaintFlags.UnderlineText;
            }

        }

        static void UpdateUi(CustomLabel view, TextView control)
        {
            if (view.FontSize > 0)
            {
                control.TextSize = (float)view.FontSize;
            }

            if (view.IsUnderlined)
            {
                control.PaintFlags = control.PaintFlags | PaintFlags.UnderlineText;
            }
        }
    }
}

iOS Renderer

[assembly: ExportRenderer(typeof(CustomLabel), typeof(CustomLabelRenderer))]
namespace Incident.iOS.CustomRenderers
{
    public class CustomLabelRenderer : LabelRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);

            var view = (CustomLabel)Element;

            UpdateUi(view, Control);
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            var view = (CustomLabel)Element;

            if (e.PropertyName == CustomLabel.IsUnderlinedProperty.PropertyName)
            {
                UpdateUi(view, Control);
            }
        }

        private static void UpdateUi(CustomLabel view, UILabel control)
        {
            var attrString = new NSMutableAttributedString(control.Text);

            if (view != null && view.IsUnderlined)
            {
                attrString.AddAttribute(UIStringAttributeKey.UnderlineStyle,
                    NSNumber.FromInt32((int)NSUnderlineStyle.Single),
                    new NSRange(0, attrString.Length));
            }

            control.AttributedText = attrString;
        }
    }
}
Steve Chadbourne
  • 6,873
  • 3
  • 54
  • 82