skip to Main Content

Using Visual Studio Community edition 2022.

New to .Net MAUI and am trying to follow the examples provided in the documentation found at https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/label

The XAML code below (from the docs) is used to create a "link" using a tap recognizer:

     <Label>
        <Label.FormattedText>
           <FormattedString>
              <Span 
                 Text="Link: " 
              />
              <Span 
                 Text="click here to open the docs"
                 TextColor="Blue"
                 TextDecorations="Underline">
                 <Span.GestureRecognizers>
                    <TapGestureRecognizer
                       Command="{Binding OpenUrlCommand}"
                       CommandParameter="https://learn.microsoft.com/dotnet/maui/" 
                    />
                 </Span.GestureRecognizers>
              </Span>
           </FormattedString>
        </Label.FormattedText>
     </Label>

However, the command (OpenUrlCommand) is never called – tested using the Windows and Andriod emulators.

In my ViewModel, I define the OpenUrlCommand as follows:

public ICommand OpenUrlCommand => new Command<string>( async ( url ) => await Launcher.OpenAsync( url ) );

… but nothing works, I also tried the following — no go …

public ICommand OpenUrlCommand => new Command<string>( async ( url ) => await Browser.OpenAsync( url ) );

… and then tried the following — again, no luck …

public IRelayCommand OpenUrlCommand => new RelayCommand<string>( async ( url ) => await Launcher.OpenAsync( url ) );

I then replaced the call to open the url with a Debug.WriteLine( url ), and it seems that the OpenUrlCommand is never being called. Also, one would expect the cursor to change when hovering over the "link" text, but it never does — it is as though the TapGesterRecognizer is not being created or registered.

As a last ditch effort, I also tried the following (again, copying from the docs):

<Label TextType="Html">
    <![CDATA[
       This is <a href="https://learn.microsoft.com/dotnet/maui/" target="_blank">a link to another site</a>.
    ]]>
 </Label>

… no luck — the link appears underlined, but when clicked on, nothing happens (cursor does not change, browser does not open).

Any advice, or even better, a working example would be appreciated.

UPDATE:
Found this post as well: https://github.com/dotnet/maui/issues/4734 – which looks like this was a known bug back in February!

As per @Jessie Zhang -MSFT, I ended up doing the following – which underlines the link, but with the caveat that the WHOLE label is tapable …

     <!-- 
     
     This works - HOWEVER, the WHOLE label is tappable.  We cannot use
     Span.GestureRecognizers because of the following issue:
     
     https://github.com/dotnet/maui/issues/4734
     
     -->

     <Label>
        <Label.GestureRecognizers>
           <TapGestureRecognizer 
              Command="{Binding OpenUrlCommand}"
              CommandParameter="https://learn.microsoft.com/en-us/dotnet/maui/" 
           />
        </Label.GestureRecognizers>
        <Label.FormattedText>
           <FormattedString>
              <Span 
                 Text="To learn more, " 
              />
              <Span 
                 Text="check the documentation"
                 TextColor="Blue"
                 TextDecorations="Underline">
              </Span>
              <Span 
                 Text="." 
              />
           </FormattedString>
        </Label.FormattedText>
     </Label>

… and in my ViewModel:

  public IRelayCommand OpenUrlCommand => new RelayCommand<String>( launch_browser );


  private async void launch_browser( String url )
  {

     Debug.WriteLine( $"*** Tap: {url}" );

     await Browser.OpenAsync( url );

  }

… the above works (for me) in the Windows and Andriod simulators.

4

Answers


  1. Yes, it is a known issue about this problem.

    You can follow it here: https://github.com/dotnet/maui/issues/4734 .

    But as a workaround, you can use Label.GestureRecognizers instead of Span.GestureRecognizers.

    For example:

           <Label 
            Text="click here"
            VerticalOptions="Center" 
            HorizontalOptions="Center" >
    
            <Label.GestureRecognizers>
                <TapGestureRecognizer Command="{Binding TapCommand}"
                                          CommandParameter="https://learn.microsoft.com/dotnet/maui/" />
            </Label.GestureRecognizers>
    
        </Label>
    
    Login or Signup to reply.
  2. This is my solution – I had a look at this section of the Microsoft docs which adds functionality to a Span but it made more sense for me to extend the Label control in the same way:

    namespace MyApp.Controls
    {
        public class HyperlinkLabel : Label
        {
            public static readonly BindableProperty UrlProperty =
                BindableProperty.Create(nameof(Url), typeof(string), typeof(HyperlinkLabel), null);
    
            public string Url
            {
                get { return (string)GetValue(UrlProperty); }
                set { SetValue(UrlProperty, value); }
            }
    
            public HyperlinkLabel()
            {
                TextDecorations = TextDecorations.Underline;
                TextColor = Colors.Blue;
                GestureRecognizers.Add(new TapGestureRecognizer
                {
                    // Launcher.OpenAsync is provided by Essentials.
                    Command = new Command(async () => await Launcher.OpenAsync(Url))
                });
            }
        }
    }
    

    Then in XAML:

    xmlns:controls="clr-namespace:MyApp.Controls"
    
    ...
    
    <controls:HyperlinkLabel
        Text="Click me"
        Url="https://learn.microsoft.com/dotnet/" />
    

    I’m new to XAML and MAUI so would be happy for any correction/input.

    Login or Signup to reply.
  3. This is/was a problem in Xamarin.Forms as well, and I suspect the underlying issue is the same. I came across the issue a couple of years ago when implementing a custom chatbot.

    The problem is caused by Label being a single UI object and how FormattedText and the associated spans are implemented in the Label renderers (I’m assuming the .Net MAUI Handlers have the same issue). The problem is that the Label is a single UI object and there is no two dimensional position information calculated and associated with a span, so there is no way to associate a gesture recognizer with either a separate UI object for the span or directly with coordinates on the screen. The Xamarin.Forms implementation uses the latter approach and tries to calculate the screen coordinates of the span, however it’s an estimation process which is flawed, usually resulting in the actual active tap area being incorrect and inconsistent. The only way I got something working consistently was to place the link text in a separate label and associate the gesture recognizer with that (as has been suggested in a previous answer). In our chatbot use case, this meant splitting messages around links and displaying all links on their own line(s) – they cant be true in-line links. This work around was acceptable to the client.

    This is a fundamentally difficult problem to solve, given the way that the Label renderers/handlers have been implemented. If you must have true in-line links, it may be best to create a new type of Label and create custom renderers/handlers for it that it utilize the way the use case is handled on each platform. On Android this might entail using TextView, mapping the text to HTML and setting the MovementMethod (https://learntodroid.com/how-to-create-a-hyperlink-using-android-textview/), and on iOS using UITextView with NSAttributedStrings, or subclassing UILabel and creating a custom implementation (https://augmentedcode.io/2020/12/20/opening-hyperlinks-in-uilabel-on-ios/). Another approach might be to use an embedded WebView and pass it the content in html format, maybe even embedding javascript in it depending on the use case.

    Obviously, if you can accept having your links on a separate line rather than be truly in-line, then using a separate Label object will be far easier.

    Login or Signup to reply.
  4. I figured out how to do this in xamarin with an android renderer by setting the native textview’ AutoLinkMask property to All. I didn’t need to do this for ios because it already seemed to work.

    textView.AutoLinkMask = MatchOptions.All;
    

    In maui we can do similar. This will turn this on for ALL labels in the application

    #if ANDROID
                    Microsoft.Maui.Handlers.LabelHandler.Mapper.AppendToMapping("AutoLinkMask", (h,e) => {
                        h.PlatformView.AutoLinkMask = Android.Text.Util.MatchOptions.All;
                    });
    #endif
    

    I put this code in my MauiProgram.cs file, but apparently it can work in other locations.

    Now if the text set to the label contains a properly formatted url, the url will become a link, and clicking on the link will open the browser on the device. No extra click handling required!

    Edit:
    Seems this approach is not without its issues. I had a label with a link in a list’s item template and opened the link instead of clicking the item.
    The link is also not rendering consistently in my collectionView of my chat messages.
    Hoping this approach may help inspire someone with a better idea though. Best of luck!

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search