Ссылки на содержимое в текстовых элементах управления

Ссылки на содержимое позволяют вставлять в текстовые элементы управления форматированные данные. Благодаря этому пользователь может находить и использовать больше информации о людях и местах, не покидая вашего приложения.

Важно!

Функции Windows, которые включают ссылки на содержимое, недоступны в версиях Windows выше Windows 10 версии 1903. Ссылки на содержимое для текстовых элементов управления XAML не будут работать в версиях Windows выше 1903.

Когда пользователь использует в RichEditBox символ \@ в качестве префикса, отображается список людей и (или) мест, соответствующих вводу. Затем, например, когда пользователь выбирает место, ContentLink для этого места вставляется в текст. Когда пользователь вызывает ссылку на содержимое из RichEditBox, отображается всплывающий элемент с картой и дополнительными сведениями о месте.

Важные API: класс ContentLink, класс ContentLinkInfo, класс RichEditTextRange

Примечание.

API для ссылок на содержимое распределяются по следующим пространствам имен: Windows.UI.Xaml.Controls, Windows.UI.Xaml.Documents и Windows.UI.Text.

Существует два разных способа использования ссылок на содержимое.

  1. В RichEditBox пользователь может открыть средство выбора для добавления ссылки на содержимое путем размещения символа "@" перед текстом. Ссылка на содержимое хранится как часть содержимого форматированного текста.
  2. В TextBlock или RichTextBlock ссылка на содержимое является текстовым элементом, который можно использовать как элемент Hyperlink.

Вот как ссылки на содержимое выглядят по умолчанию в элементах RichEditBox и TextBlock.

content link in rich edit boxcontent link in text block

Отличия в использовании, отображении и поведении подробно описаны в следующих разделах. В этой таблице показано краткое сравнение основных различий между ссылкой на содержимое в элементе RichEditBox и текстовом блоке.

Компонент RichEditBox Текстовый блок
Использование Пример ContentLinkInfo Текстовый элемент ContentLink
Курсор Определен типом ссылки на содержимое; изменение невозможно Определен свойством Cursor; значение null по умолчанию
Подсказка Не отображается Отображает дополнительный текст

Наиболее распространенная практика использования ссылки на содержимое — это предоставление пользователю возможности быстро добавлять информацию путем постановки символа амперсанда (@) перед именем человека или названием места в тексте. При включении ссылок на содержимое в элементе RichEditBox открывается средство выбора, которое позволяет пользователю вставить человека из списка контактов или ближайшее место, в зависимости от того, какая функция была включена.

Ссылку на содержимое можно сохранить с содержимым форматированного текста, и можно извлечь ее для использования в других частях приложения. Например, в приложении для работы с электронной почтой можно извлечь данные о человеке и использовать их, чтобы заполнить поле "Кому:" адресом электронной почты.

Примечание.

Средство выбора для ссылок на содержимое — это приложение, которое входит в состав Windows, поэтому оно запускается в отдельном от вашего приложения процессе.

Включение ссылок на содержимое в элементе RichEditBox осуществляется путем добавления одного или нескольких поставщиков ссылок на содержимое в коллекцию RichEditBox.ContentLinkProviders. Существует 2 поставщика ссылок на содержимое, которые встроены в платформу XAML.

Важно!

Значением по умолчанию для свойства "RichEditBox.ContentLinkProviders" является null, а не пустая коллекция. Перед добавлением поставщиков ссылок на содержимое необходимо явно создать ContentLinkProviderCollection .

Вот как добавлять поставщиков ссылок на содержимое в XAML-код.

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

Можно также добавить поставщиков ссылок на содержимое в элемент Style и применить его к нескольким элементам RichEditBoxes следующим образом.

<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 можно задать свойства ContentLinkForegroundColor и ContentLinkBackgroundColor, чтобы изменить цвета по умолчанию.

Цвет курсора задать нельзя. Курсор отрисовывается элементом RichEditbox в зависимости от типа ссылки на содержимое. Person — курсор для ссылки на человека или Pin — курсор для ссылки на место.

Объект ContentLinkInfo

Когда пользователь выбирает человека или место с помощью средства выбора, система создает объект ContentLinkInfo и добавляет его в свойство ContentLinkInfo текущего элемента RichEditTextRange .

Объект ContentLinkInfo содержит сведения, используемые для отображения, вызова и администрирования ссылки на содержимое.

  • DisplayText — это строка, которая отображается при визуализации ссылки на содержимое. В RichEditBox пользователь может редактировать текст ссылки на содержимое после его создания, которое изменяет значение этого свойства.
  • SecondaryText — эта строка отображается в подсказке отображаемой ссылки на содержимое.
    • В ссылке на содержимое Place, созданной с помощью средства выбора, содержится адрес места (если он доступен).
  • URI — ссылка на дополнительные сведения об объекте ссылки на содержимое. Этот URI может открыть установленное приложение или веб-сайт.
  • Id — это нередактируемый параметр, который используется для каждого элемента управления и создается счетчиком элемента управления RichEditBox. Он позволяет отслеживать сведения ContentLinkInfo во время выполнения действий, таких как удаление или изменение. Если ContentLinkInfo вырезается и вставляется обратно в элемент управления, он получит новый идентификатор. Значения идентификатора являются добавочными.
  • LinkContentKind — строка, которая описывает тип ссылки на содержимое. Встроенные типы содержимого: Places и Contacts. Значение обрабатывается с учетом регистра.

Существует несколько ситуаций, когда важен элемент LinkContentKind.

  • Когда пользователь копирует ссылку на содержимое из RichEditBox и вставляет ее в другой элемент RichEditBox, у обоих элементов управления должен быть ContentLinkProvider для данного типа содержимого. В противном случае ссылка вставляется в виде текста.
  • LinkContentKind можно использовать в обработчике событий ContentLinkChanged, чтобы определить, что следует делать со ссылкой на содержимое при ее использовании в других частях приложения. См. дополнительные сведения в разделе "Пример".
  • LinkContentKind влияет на то, как система открывает URI при вызове ссылки. Это будет рассмотрено далее, в обсуждении запуска URI.

Запуск URI

Свойство URI работает как свойство NavigateUri гиперссылки. Когда пользователь щелкает его, происходит запуск URI в браузере по умолчанию или в приложении, которое зарегистрировано для конкретного протокола, указанного в значении URI.

Здесь описываются особенности поведения двух встроенных типов ссылок на содержимое.

Размещает

Средство выбора Places создает ContentLinkInfo с корнем URI https://maps.windows.com/. Эту ссылку можно открыть тремя способами.

  • Если LinkContentKind = "Places", открывается карточка сведений во всплывающем элементе. Всплывающий элемент аналогичен всплывающему элементу средства выбора ссылки на содержимое. Он является частью Windows и запускается в виде отдельного процесса из приложения.
  • Если значением LinkContentKind не является "Places", происходит попытка открыть приложение Карты в указанном расположении. Например, это может произойти, если вы изменили LinkContentKind в обработчике событий ContentLinkChanged.
  • Если не удается открыть URI в приложении "Карты", то карта открывается в браузере по умолчанию. Как правило, это происходит, когда пользовательские параметры раздела Приложения для веб-сайтов запрещают открытие URI с помощью приложения Карты.
Люди

Средство выбора People создает ContentLinkInfo с URI, который использует протокол ms-people.

  • Если LinkContentKind = "People", открывается карточка сведений во всплывающем элементе. Всплывающий элемент аналогичен всплывающему элементу средства выбора ссылки на содержимое. Он является частью Windows и запускается в виде отдельного процесса из приложения.
  • Если значение LinkContentKind отлично от "People", открывается приложение Люди. Например, это может произойти, если вы изменили LinkContentKind в обработчике событий ContentLinkChanged.

Совет

Для получения дополнительной информации об открытии других приложений и веб-сайтов из вашего приложения см. раздел Запуск приложения с помощью URI.

Invoked

Когда пользователь вызывает ссылку на содержимое, событие ContentLinkInvoked создается до возникновения поведения по умолчанию — то есть запуска URI. Вы можете обработать это событие, чтобы переопределить или отменить поведение по умолчанию.

В этом примере показано, как переопределить поведение запуска по умолчанию для ссылки на содержимое Place. Вместо открытия карты в карточке сведений Place, приложении "Карты" или веб-браузере по умолчанию можно пометить событие как "Handled" и открыть карту во встроенном в приложение элементе управления 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

Событие ContentLinkChanged можно использовать, чтобы получать уведомление при добавлении, изменении или удалении ссылки на содержимое. Это позволяет извлекать ссылку на содержимое из текста и использовать ее в других местах в вашем приложении. Об этом рассказывается далее в разделе "Примеры".

Свойства ContentLinkChangedEventArgs.

  • ChangedKind — это перечисление ContentLinkChangeKind, которое является действием в ContentLink. Например, этим действием может быть вставка, удаление или изменение ContentLink.
  • Info — элемент ContentLinkInfo, который был целью действия.

Это событие вызывается для каждого действия ContentLinkInfo. Например, если пользователь копирует и вставляет одновременно несколько ссылок на содержимое в RichEditBox, это событие вызывается для каждого добавленного элемента. Или если пользователь выбирает и удаляет несколько ссылок на содержимое одновременно, это событие вызывается для каждого удаленного элемента.

Это событие вызывается для RichEditBox после вызова события TextChanging и перед вызовом события TextChanged.

Свойство 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.
  • Ссылка на содержимое для места, создаваемого в коде.

В других случаях, как правило, лучше использовать текстовый элемент Hyperlink.

Внешний вид ссылки на содержимое определяется ее передним планом, фоном и курсором. В текстовом блоке можно задать свойства Foreground (из TextElement) и Background, чтобы изменить цвета по умолчанию.

По умолчанию курсор Hand отображается в том случае, когда пользователь наводит его на ссылку на содержимое. В отличие от RichEditBlock элементы управления текстовыми блоками не изменяют курсор автоматически в зависимости от типа ссылки. Свойство Cursor можно настроить для изменения типа курсора в зависимости от типа ссылки или других факторов.

Вот пример ContentLink в элементе TextBlock. ContentLinkInfo создается в коде и назначается свойству Info текстового элемента ContentLink, созданного в XAML.

<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 в текстовом элементе управления в XAML разместите содержимое в контейнере Span и примените атрибут xml:space="preserve" к Span для сохранения пробела между ContentLink и другими элементами.

Примеры

В этом примере пользователь может добавить в RickTextBlock ссылку на содержимое о человеке или месте. Обработка события ContentLinkChanged осуществляется для извлечения ссылок на содержимое и сохранения их в актуальном состоянии в списке людей или в списке мест.

В шаблонах элементов для списков TextBlock используется с текстовым элементом ContentLink для отображения сведений ссылки на содержимое. Элемент ListView задает собственный фон для каждого элемента, поэтому для элемента ContentLink задан фон Transparent (прозрачный).

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