Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Partial text clickable in Xamarin Label

I am looking for a way to make parts of the text of a Label in Xamarin Forms clickable, performing different functions, and ideally have different formatting. I understand that a clickable Label is straightforward and can be done something like this:

Label label = new Label { Text = "Click me" };
label.Click += () => { label.Text = "Clicked"; };

Essentially, I'm looking to mimic behavior in an app that I've done in native Android. My app must work for Android and iOS and ideally UWP. In Android, CharSequence objects can be added to a TextView (the control similar to Label). In Android, I might create a CharSequence object like this:

    Runnable doSomething; //equivalent to C# Action; assuming initialized somewhere
    String myString = "My Clickable String"; //the clickable string
    Spannable span = Spannable.Factory.getInstance().newSpannable(myString); //this is the clickable text to add to TextView
        span.setSpan(new ClickableSpan() {
            @Override
            public void onClick(View v) {
                doSomething.run(); //perform the action
            }

            @Override
            public void updateDrawState(TextPaint ds) {
                ds.setUnderlineText(true); //make underlined
                ds.setColor(Color.rgb(color.r, color.g, color.b)); //change the color
                ds.setFakeBoldText(true); //make it bold
                ds.setTextSize((float) largeFont); //change the text size
            }
        }, 0, a.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); //style and function for the entire string

As you can see, it performs a function and has style attributes. I can then add this to TextView (Spannable implements Spanned which implements CharSequence). I can add multiple CharSequences in addition to non-clickable Strings as I desire.

I'm hoping for a way that I can do this in Xamarin but I haven't found a solution yet. I've considered the possibility of custom renderers though I feel that a cross-platform solution would be preferable, if possible. I'm far less familiar with iOS but it seems that it's possible to add NSMutableAttributedString objects to a UILabel to similar effect. I've also seen the FancyLabel object. I'm not quite sure how I would do this for UWP as I really didn't find much in my research.

Any assistance would be very appreciated!

like image 789
horski Avatar asked Nov 14 '16 15:11

horski


3 Answers

Just to provide an updated answer to this using Xamarin Forms 3.4 (and over). You can use the FormattedText property of the Label. There is an in-depth overview on how to use it over in the Xamarin Docs.

Fundamentally, it works like so:

<Label 
    LineBreakMode="WordWrap">
    <Label.FormattedText>
        <FormattedString>
            <Span 
                Text="Red Bold, " 
                TextColor="Red" 
                FontAttributes="Bold" />
            <Span 
                Text="default, " 
                Style="{DynamicResource BodyStyle}">                          
                <Span.GestureRecognizers>
                    <TapGestureRecognizer Command="{Binding TapCommand}" />
                </Span.GestureRecognizers>
            </Span>
            <Span 
                Text="italic small." 
                FontAttributes="Italic" 
                FontSize="Small" />
        </FormattedString>
    </Label.FormattedText>
</Label>

In the above, tapping on the text default will execute TapCommand.

like image 101
Tom Avatar answered Sep 28 '22 18:09

Tom


This answer may be extremely after-the-fact but may help others in the future.

What I've done and tested is to use a grid with your string split up appropriately, say you want to change the behaviour of a word:
In your ViewModel/View:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/> //for first few words
        <ColumnDefinition Width="Auto"/> //the target word
        <ColumnDefinition Width="Auto"/> //the rest of the string
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="*"/> //one row for one line of text
    </Grid.RowDefinitions>

    <Label Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="0" Text="First part of string"/>
    <Label Grid.Column="1" Grid.ColumnSpan="1" Grid.Row="0" Text="TARGET-WORD">
        <Label.GestureRecognizers>
            <TapGestureRecognizer Command="{Binding <YOUR_COMMAND>}"/>
        </Label.GestureRecognizers>  
    </Label>
    <Label Grid.Column="2" Grid.ColumnSpan="1" Grid.Row="0" Text="rest of the string"/>
</Grid>

This way you can isolate specific portions of grid and amend behaviour/aethetics (data triggers, styling, gestures). Remember that <Span></Span> tags do not support all the features of a <Label></Label>.

Clearly, in XAML you would need to know the position of the word, or if the string is dynamic, you could dynamically search for it and also construct your Grid in the code behind.

*Tested on Xamarin.Forms 2.3.4.247

like image 45
ethane Avatar answered Sep 28 '22 18:09

ethane


Considering an example to answer this question which may help somebody in future,

Eg. i need to write "By signing up, you agree to the Terms of Services and Privacy Policy" with some formatting:

  1. Terms of Services with different color and can be tapped
  2. Privacy Policy with different color and can be tapped

Rest part of the string should be simple.

Code in XAML:

`<Label Text="By signing up, you agree to the " Grid.Row="0"/>
 <Label Text="Terms of Services" TextColor="DarkOrange" WidthRequest="150" 
        HorizontalOptions="End" Grid.Row="0"> 
     <Label.GestureRecognizers>
         <TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped_Terms"/>
     </Label.GestureRecognizers>
 </Label>
 <Label Text=" and " HorizontalOptions="End"  Grid.Row="0" />
 <Label Text="Privacy Policy" TextColor="DarkOrange" HorizontalOptions="Center"
        VerticalOptions="CenterAndExpand" Grid.Row="0">
     <Label.GestureRecognizers>
         <TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped_Policy"/>
     </Label.GestureRecognizers>
 </Label>
`

Code in C# for tapped callbacks:

private void TapGestureRecognizer_Tapped_Terms(object sender, EventArgs e)
{
    Navigation.PushAsync(new TermsofServicesPage());
}

private void TapGestureRecognizer_Tapped_Policy(object sender, EventArgs e)
{
    Navigation.PushAsync(new PrivacyPolicyPage());
}

Different formattings of the string can be performed and works fine for cross platforms.

like image 22
shubham aggarwal Avatar answered Sep 28 '22 18:09

shubham aggarwal