文本控件中的内容链接

内容链接提供了在文本控件中嵌入丰富数据的方法,让用户无需离开应用的上下文即可查找和使用有关人员或位置的详细信息。

重要

启用内容链接的 Windows 功能在 Windows 10 版本 1903 之后的 Windows 版本中不可用。 XAML 文本控件的内容链接在低于版本 1903 的 Windows 版本中不起作用。

当用户在 RichEditBox 中为带有 at(@) 符号的条目添加前缀时,会显示与条目匹配的人员列表和/或放置建议。 例如,当用户选取某个位置时,会将该位置的 ContentLink 插入到文本中。 当用户从 RichEditBox 调用内容链接时,将显示一个浮出控件,其中包含地图和有关该位置的其他信息。

重要 APIContentLink 类ContentLinkInfo 类RichEditTextRange 类

注释

内容链接的 API 分布在以下命名空间中:Windows.UI.Xaml.Controls、Windows.UI.Xaml.Documents 和 Windows.UI.Text。

使用内容链接有两种不同的方法:

  1. RichEditBox中,用户可以通过在文本前加上 @ 符号来打开选择器以添加内容链接。 内容链接作为格式文本内容的一部分存储。
  2. TextBlockRichTextBlock 中,内容链接是具有用法和行为的文本元素,与 超链接非常类似。

以下是默认情况下内容链接在 RichEditBox 和 TextBlock 中的外观。

富编辑框中的内容链接 文本块中的内容链接

以下各节详细介绍了使用情况、呈现和行为的差异。 此表快速比较 RichEditBox 中内容链接与文本块之间的主要差异。

功能 / 特点 RichEditBox 文本块
用法 ContentLinkInfo 实例 ContentLink 文本元素
游标 内容链接的类型由其决定,无法更改。 由 Cursor 属性确定,默认情况下 null
工具提示 未显示 显示辅助文本

内容链接的最常见用途是让用户在其文本中以与号(@)符号为人员或地名添加前缀来快速添加信息。 在 RichEditBox中启用时,这将打开一个选取器,并允许用户从联系人列表中插入人员或附近地点,具体取决于您启用了哪种功能。

内容链接可以与富文本内容一起保存,并且你可以将其提取以用于应用的其他部分。 例如,在电子邮件应用中,可以提取人员信息,并将其用于用电子邮件地址填充收件人框。

注释

内容链接选取器是 Windows 的一部分,因此它在与你的应用不同的进程中运行。

通过将一个或多个内容链接提供程序添加到 RichEditBox.ContentLinkProviders 集合来启用 RichEditBox 中的内容链接。 XAML 框架中内置了 2 个内容链接提供程序。

重要

RichEditBox.ContentLinkProviders 属性的默认值为 null,而不是空集合。 在添加内容链接提供程序之前,需要显式创建 ContentLinkProviderCollection

下面介绍如何在 XAML 中添加内容链接提供程序。

<RichEditBox>
    <RichEditBox.ContentLinkProviders>
        <ContentLinkProviderCollection>
            <ContactContentLinkProvider/>
            <PlaceContentLinkProvider/>
        </ContentLinkProviderCollection>
    </RichEditBox.ContentLinkProviders>
</RichEditBox>

还可以在 Style 中添加内容链接提供程序,并将其应用于多个 RichEditBox,如下所示。

<Page.Resources>
    <Style TargetType="RichEditBox" x:Key="ContentLinkStyle">
        <Setter Property="ContentLinkProviders">
            <Setter.Value>
                <ContentLinkProviderCollection>
                    <PlaceContentLinkProvider/>
                    <ContactContentLinkProvider/>
                </ContentLinkProviderCollection>
            </Setter.Value>
        </Setter>
    </Style>
</Page.Resources>

<RichEditBox x:Name="RichEditBox01" Style="{StaticResource ContentLinkStyle}" />
<RichEditBox x:Name="RichEditBox02" Style="{StaticResource ContentLinkStyle}" />

下面介绍如何在代码中添加内容链接提供程序。

RichEditBox editor = new RichEditBox();
editor.ContentLinkProviders = new ContentLinkProviderCollection
{
    new ContactContentLinkProvider(),
    new PlaceContentLinkProvider()
};

内容链接的外观由其前景、背景和图标决定。 在 RichEditBox 中,可以设置 ContentLinkForegroundColorContentLinkBackgroundColor 属性以更改默认颜色。

无法设置光标。 光标由 RichEditbox 根据内容链接的类型呈现——对于人员链接是 人员 光标,对于地点链接是 地点 光标。

ContentLinkInfo 对象

当用户从人员或位置选取器中进行选择时,系统会创建 ContentLinkInfo 对象并将其添加到当前 RichEditTextRangeContentLinkInfo 属性。

ContentLinkInfo 对象包含用于显示、调用和管理内容链接的信息。

  • DisplayText – 这是呈现内容链接时显示的字符串。 在 RichEditBox 中,用户可以在创建内容链接后编辑内容链接的文本,这会更改此属性的值。
  • SecondaryText – 此字符串显示在已渲染的内容链接 工具提示 中。
    • 在选取器创建的“位置”内容链接中,它包含位置的地址(如果可用)。
  • Uri – 指向有关内容链接主题的详细信息的链接。 此 URI 可以打开已安装的应用或网站。
  • ID - 这是 RichEditBox 控件创建的只读的、每个控件的计数器。 它用于在删除或编辑等操作期间跟踪此 ContentLinkInfo。 如果 ContentLinkInfo 被剪切并粘贴回控件,它将获得新的 ID。ID 值是增量的。
  • LinkContentKind – 描述内容链接类型的字符串。 内置内容类型 位置联系人。 该值区分大小写。

在以下情况下,LinkContentKind 非常重要。

  • 当用户从 RichEditBox 复制内容链接并将其粘贴到另一个 RichEditBox 时,这两个控件必须具有该内容类型的 ContentLinkProvider。 否则,链接将粘贴为文本。
  • 可以在 ContentLinkChanged 事件处理程序中使用 LinkContentKind 来确定在应用的其他部分使用内容链接时应采取什么行动。 有关详细信息,请参阅“示例”部分。
  • LinkContentKind 影响调用链接时系统打开 Uri 的方式。 我们将在下一步启动 URI 的讨论中看到这一点。

URI 启动

Uri 属性的工作方式与 Hyperlink 的 NavigateUri 属性非常类似。 当用户单击该 URI 时,它会在默认浏览器中启动 URI,或在为 Uri 值中指定的特定协议注册的应用中启动 URI。

此处介绍了 2 种内置链接内容的特定行为。

地点

Places 选取器使用 uri 根目录 https://maps.windows.com/创建 ContentLinkInfo。 可以通过 3 种方式打开此链接:

  • 如果 LinkContentKind = “Places”,则会在浮出窗口中打开信息卡。 浮动窗口类似于内容链接选择器浮动窗口。 它是 Windows 的一部分,在与应用不同的进程中运行。
  • 如果 LinkContentKind 不是“Places”,它将尝试将 地图 应用打开到指定位置。 例如,如果在 ContentLinkChanged 事件处理程序中修改了 LinkContentKind,则可能会发生这种情况。
  • 如果无法在 Maps 应用中打开 Uri,则会在默认浏览器中打开地图。 当用户的网站 应用 设置不允许使用 地图 应用打开 Uri 时,通常会发生这种情况。
人员

人员选取器使用 ms-people 协议的 Uri 来创建 ContentLinkInfo。

  • 如果 LinkContentKind = “人员”,则会在弹出窗口中打开信息卡。 浮动窗口类似于内容链接选择器浮动窗口。 它是 Windows 的一部分,在与应用不同的进程中运行。
  • 如果 LinkContentKind 不是“People”,则会打开 People 应用程序。 例如,如果在 ContentLinkChanged 事件处理程序中修改了 LinkContentKind,则可能会发生这种情况。

小窍门

有关从您的应用打开其他应用和网站的更多信息,请参阅主题 "使用 Uri 启动应用"

调用

当用户调用内容链接时,在启动 Uri 的默认行为之前,将引发 ContentLinkInvoked 事件。 可以处理此事件以替代或取消默认行为。

此示例演示如何替代“位置”内容链接的默认启动行为。 而不是在“位置”信息卡、“地图”应用或默认 Web 浏览器中打开地图,你将事件标记为“已处理”,并在应用内 WebView 控件中打开地图。

<RichEditBox x:Name="editor"
             ContentLinkInvoked="editor_ContentLinkInvoked">
    <RichEditBox.ContentLinkProviders>
        <ContentLinkProviderCollection>
            <PlaceContentLinkProvider/>
        </ContentLinkProviderCollection>
    </RichEditBox.ContentLinkProviders>
</RichEditBox>

<WebView x:Name="webView1"/>
private void editor_ContentLinkInvoked(RichEditBox sender, ContentLinkInvokedEventArgs args)
{
    if (args.ContentLinkInfo.LinkContentKind == "Places")
    {
        args.Handled = true;
        webView1.Navigate(args.ContentLinkInfo.Uri);
    }
}

内容链接已更改

添加、修改或删除内容链接时,可以使用 ContentLinkChanged 事件通知。 这样,你可以从文本中提取内容链接,并在应用中的其他地方使用它。 稍后将在“示例”部分显示这一点。

ContentLinkChangedEventArgs 的属性包括:

  • ChangedKind - 此 ContentLinkChangeKind 枚举是 ContentLink 中的操作。 例如,如果 ContentLink 被插入、删除或编辑。
  • 信息 - 操作所针对的 ContentLinkInfo。

为每个 ContentLinkInfo 操作触发此事件。 例如,如果用户同时将多个内容链接复制并粘贴到 RichEditBox 中,则会为每个添加的项目引发此事件。 或者,如果用户同时选择并删除多个内容链接,则会为每个已删除的项目引发此事件。

TextChanging 事件之后和 TextChanged 事件之前,该事件在 RichEditBox 上被引发。

可以使用 RichEditTextRange.ContentLink 属性从丰富的编辑文档获取内容链接。 TextRangeUnit 枚举具有 ContentLink 值,用于将内容链接指定为导航文本范围时要使用的单元。

此示例演示如何枚举 RichEditBox 中的所有内容链接,以及如何将人员提取到列表中。

<StackPanel Width="300">
    <RichEditBox x:Name="Editor" Height="200">
        <RichEditBox.ContentLinkProviders>
            <ContentLinkProviderCollection>
                <ContactContentLinkProvider/>
                <PlaceContentLinkProvider/>
            </ContentLinkProviderCollection>
        </RichEditBox.ContentLinkProviders>
    </RichEditBox>

    <Button Content="Get people" Click="Button_Click"/>

    <ListView x:Name="PeopleList" Header="People">
        <ListView.ItemTemplate>
            <DataTemplate x:DataType="ContentLinkInfo">
                <TextBlock>
                    <ContentLink Info="{x:Bind}" Background="Transparent"/>
                </TextBlock>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</StackPanel>
private void Button_Click(object sender, RoutedEventArgs e)
{
    PeopleList.Items.Clear();
    RichEditTextRange textRange = Editor.Document.GetRange(0, 0) as RichEditTextRange;

    do
    {
        // The Expand method expands the Range EndPosition 
        // until it finds a ContentLink range. 
        textRange.Expand(TextRangeUnit.ContentLink);
        if (textRange.ContentLinkInfo != null
            && textRange.ContentLinkInfo.LinkContentKind == "People")
        {
            PeopleList.Items.Add(textRange.ContentLinkInfo);
        }
    } while (textRange.MoveStart(TextRangeUnit.ContentLink, 1) > 0);
}

若要在 TextBlock 或 RichTextBlock 控件中使用内容链接,请使用 ContentLink 文本元素(从 Windows.UI.Xaml.Documents 命名空间)。

文本块中 ContentLink 的典型源包括:

  • 由从 RichTextBlock 控件中提取的选取器创建的内容链接。
  • 在代码中创建的位置的内容链接。

对于其他情况,超链接文本元素通常适用。

内容链接的外观由其前景、背景和光标确定。 在文本块中,可以通过将前景(来自 TextElement)和背景的 属性设置来更改默认颜色。

默认情况下,当用户将鼠标悬停在内容链接上时,将显示 Hand 光标。 与 RichEditBlock 不同,文本块控件不会根据链接类型自动更改光标。 可以将 Cursor 属性设置为基于链接类型或其他因素更改游标。

下面是 TextBlock 中使用的 ContentLink 示例。 ContentLinkInfo 是在代码中创建的,并分配给在 XAML 中创建的 ContentLink 文本元素的 Info 属性。

<StackPanel>
    <TextBlock>
        <Span xml:space="preserve">
            <Run>This volcano erupted in 1980: </Run><ContentLink x:Name="placeContentLink" Cursor="Pin"/>
            <LineBreak/>
        </Span>
    </TextBlock>

    <Button Content="Show" Click="Button_Click"/>
</StackPanel>
private void Button_Click(object sender, RoutedEventArgs e)
{
    var info = new Windows.UI.Text.ContentLinkInfo();
    info.DisplayText = "Mount St. Helens";
    info.SecondaryText = "Washington State";
    info.LinkContentKind = "Places";
    info.Uri = new Uri("https://maps.windows.com?cp=46.1912~-122.1944");
    placeContentLink.Info = info;
}

小窍门

在文本控件中将 ContentLink 与其他文本元素一起使用时,请将内容放置在 Span 容器中,并将该属性应用于 xml:space="preserve" Span,以保留 ContentLink 和其他元素之间的空白。

例子

在此示例中,用户可以输入人员或将内容链接放入 RickTextBlock。 处理 ContentLinkChanged 事件以提取内容链接,并将其 up-to日期保留在人员列表或位置列表中。

在列表的项模板中,将 TextBlock 与 ContentLink 文本元素一起使用以显示内容链接信息。 ListView 为每个项提供自己的背景,因此 ContentLink 背景设置为透明。

<StackPanel Orientation="Horizontal" Grid.Row="1">
    <RichEditBox x:Name="Editor"
                 ContentLinkChanged="Editor_ContentLinkChanged"
                 Margin="20" Width="300" Height="200"
                 VerticalAlignment="Top">
        <RichEditBox.ContentLinkProviders>
            <ContentLinkProviderCollection>
                <ContactContentLinkProvider/>
                <PlaceContentLinkProvider/>
            </ContentLinkProviderCollection>
        </RichEditBox.ContentLinkProviders>
    </RichEditBox>

    <ListView x:Name="PeopleList" Header="People">
        <ListView.ItemTemplate>
            <DataTemplate x:DataType="ContentLinkInfo">
                <TextBlock>
                    <ContentLink Info="{x:Bind}"
                                 Background="Transparent"
                                 Cursor="Person"/>
                </TextBlock>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

    <ListView x:Name="PlacesList" Header="Places">
        <ListView.ItemTemplate>
            <DataTemplate x:DataType="ContentLinkInfo">
                <TextBlock>
                    <ContentLink Info="{x:Bind}"
                                 Background="Transparent"
                                 Cursor="Pin"/>
                </TextBlock>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</StackPanel>
private void Editor_ContentLinkChanged(RichEditBox sender, ContentLinkChangedEventArgs args)
{
    var info = args.ContentLinkInfo;
    var change = args.ChangeKind;
    ListViewBase list = null;

    // Determine whether to update the people or places list.
    if (info.LinkContentKind == "People")
    {
        list = PeopleList;
    }
    else if (info.LinkContentKind == "Places")
    {
        list = PlacesList;
    }

    // Determine whether to add, delete, or edit.
    if (change == ContentLinkChangeKind.Inserted)
    {
        Add();
    }
    else if (args.ChangeKind == ContentLinkChangeKind.Removed)
    {
        Remove();
    }
    else if (change == ContentLinkChangeKind.Edited)
    {
        Remove();
        Add();
    }

    // Add content link info to the list. It's bound to the
    // Info property of a ContentLink in XAML.
    void Add()
    {
        list.Items.Add(info);
    }

    // Use ContentLinkInfo.Id to find the item,
    // then remove it from the list using its index.
    void Remove()
    {
        var items = list.Items.Where(i => ((ContentLinkInfo)i).Id == info.Id);
        var item = items.FirstOrDefault();
        var idx = list.Items.IndexOf(item);

        list.Items.RemoveAt(idx);
    }
}