.Net MAUI如何在标签中包含链接



使用Visual Studio社区版2022。

我是.Net MAUI的新手,我正在尝试遵循上的文档中提供的示例https://learn.microsoft.com/en-us/dotnet/maui/user-interface/controls/label

下面的XAML代码(来自文档)用于创建";链接";使用抽头识别器:

<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>

然而,该命令(OpenUrlCommand)从未被调用——使用Windows和Andriod模拟器进行测试。

在我的ViewModel中,我定义OpenUrlCommand如下:

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

但什么都不起作用,我也尝试了以下方法——不去。。。

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

然后又试了下面的——再一次,运气不好。。。

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

然后,我用Debug.WriteLine(url)替换了打开url的调用,似乎从来没有调用过OpenUrlCommand。同样,人们期望光标在悬停在"光标"上时改变;链接";文本,但它从来没有——就好像TapGesterRecognizer没有被创建或注册一样。

作为最后的努力,我还尝试了以下操作(再次从文档中复制):

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

运气不好——链接显示为带下划线,但当点击时,什么都不会发生(光标不会改变,浏览器不会打开)。

任何建议,甚至更好的工作榜样都将不胜感激。

更新:也发现了这篇文章:https://github.com/dotnet/maui/issues/4734-看起来这是二月份的一个已知错误!

根据@Jessie Zhang-MSFT的说法,我最终做了以下事情——强调了链接,但需要注意的是,整个标签是可点击的。。。

<!-- 

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>

在我的ViewModel中:

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

private async void launch_browser( String url )
{
Debug.WriteLine( $"*** Tap: {url}" );
await Browser.OpenAsync( url );
}

上面的工作(对我来说)在Windows和Andriod模拟器中。

是的,这是关于这个问题的已知问题。

你可以在这里关注它:https://github.com/dotnet/maui/issues/4734。

但作为一种变通方法,您可以使用Label.GestureRecognizers而不是Span.GestureRecognizers

例如:

<Label 
Text="click here"
VerticalOptions="Center" 
HorizontalOptions="Center" >
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding TapCommand}"
CommandParameter="https://learn.microsoft.com/dotnet/maui/" />
</Label.GestureRecognizers>
</Label>

这是我的解决方案-我看了一下Microsoft文档的这一部分,它为Span添加了功能,但对我来说,以同样的方式扩展Label控件更有意义:

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))
});
}
}
}

然后在XAML中:

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

我是XAML和MAUI的新手,所以很乐意接受任何更正/输入。

这是Xamarin.Forms中的一个问题,我怀疑潜在的问题也是一样的。几年前,我在实现自定义聊天机器人时遇到了这个问题。

这个问题是由Label是一个单一的UI对象以及如何在Label渲染器中实现FormattedText和相关联的跨度引起的(我假设.Net MAUI处理程序也有同样的问题)。问题是,Label是单个UI对象,并且没有计算出二维位置信息并与跨度相关联,因此无法将手势识别器与跨度的单独UI对象或直接与屏幕上的坐标相关联。Xamarin.Forms实现使用后一种方法,并试图计算跨度的屏幕坐标,但这是一个有缺陷的估计过程,通常会导致实际的活动点击区域不正确和不一致。我唯一能稳定工作的方法是将链接文本放在一个单独的标签中,并将手势识别器与之关联(正如之前的回答中所建议的那样)。在我们的聊天机器人用例中,这意味着在链接周围拆分消息,并在自己的行上显示所有链接——它们不可能是真正的内联链接。这种变通办法是客户可以接受的。

考虑到标签渲染器/处理程序的实现方式,这是一个根本难以解决的问题。如果您必须具有真正的内联链接,那么最好创建一种新类型的Label,并为其创建自定义渲染器/处理程序,以便它利用每个平台上处理用例的方式。在Android上,这可能需要使用TextView,将文本映射到HTML并设置MovementMethod(https://learntodroid.com/how-to-create-a-hyperlink-using-android-textview/),以及在iOS上使用带有NSAttributedStrings的UITextView,或子类化UILabel并创建自定义实现(https://augmentedcode.io/2020/12/20/opening-hyperlinks-in-uilabel-on-ios/)。另一种方法可能是使用嵌入式WebView并以html格式向其传递内容,甚至可能根据用例在其中嵌入javascript。

显然,如果您可以接受将链接放在单独的行上,而不是真正的内联,那么使用单独的Label对象会容易得多。

我通过将原生textview的AutoLinkMask属性设置为All,了解了如何在xamarin中使用android渲染器实现这一点。我不需要为ios做这件事,因为它似乎已经工作了。

textView.AutoLinkMask = MatchOptions.All;

在毛伊岛,我们也可以做类似的事情。这将为应用程序中的所有标签打开此选项

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

我把这个代码放在我的MauiProgram.cs文件中,但显然它可以在其他地方工作。

现在,如果设置为标签的文本包含一个格式正确的url,该url将成为一个链接,单击该链接将打开设备上的浏览器。不需要额外的点击处理!

编辑:看来这种方法并非没有问题。我在列表的项目模板中有一个带有链接的标签,并打开了链接,而不是单击该项目。该链接在我的收藏中也不一致查看我的聊天消息。不过,希望这种方法可以帮助激励有更好想法的人。祝你好运!

终于在.NET 8 Preview 5 中的.NET MAUI中解决了这个问题

。。。错误修复:实施了几个错误修复,解决了问题例如标签跨度中的手势、键盘输入问题、标签iOS上的截断、CollectionView问题、ContentView RTL等。#14410、#14382、#14453、#14391、#11763、#15114、#12909

https://devblogs.microsoft.com/dotnet/announcing-dotnet-maui-in-dotnet-8-preview-5/

最新更新