Lire en anglais

Partager via


RichSuggestBox

Le RichSuggestBox est une combinaison de AutoSuggestBox et RichEditBox qui peuvent fournir des suggestions basées sur des préfixes personnalisables. Les suggestions sélectionnées sont incorporées et suivies dans le document en tant que jetons.

RichSuggestBox ressemble aux contrôles de texte couramment trouvés dans les applications sociales où vous tapez « @ » pour mentionner des personnes.

XAML
<!--  Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE file in the project root for more information.  -->
<Page x:Class="RichSuggestBoxExperiment.Samples.RichSuggestBoxMultiplePrefixesSample"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:controls="using:CommunityToolkit.WinUI.Controls"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:local="using:RichSuggestBoxExperiment.Samples"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d">
    <Page.Resources>
        <DataTemplate x:Key="EmailTemplate">
            <StackPanel Orientation="Horizontal">
                <Border Width="20"
                        Height="20"
                        Background="{ThemeResource AccentFillColorDefaultBrush}"
                        CornerRadius="9999">
                    <TextBlock HorizontalAlignment="Center"
                               VerticalAlignment="Center"
                               FontSize="10"
                               FontWeight="Semibold"
                               Foreground="White"
                               Text="{Binding Initials}" />
                </Border>
                <TextBlock Padding="8,0,0,0"
                           Text="{Binding DisplayName}" />
            </StackPanel>
        </DataTemplate>

        <DataTemplate x:Key="DataTemplate">
            <StackPanel Orientation="Horizontal">
                <SymbolIcon Symbol="{Binding Icon}" />
                <TextBlock Margin="8,0,0,0"
                           Text="{Binding Text}" />
            </StackPanel>
        </DataTemplate>

        <DataTemplate x:Key="TokenTemplate">
            <StackPanel Margin="0,4,0,12"
                        Orientation="Vertical">
                <TextBlock>
                    <Run Text="Text:" />
                    <Run FontWeight="SemiBold"
                         Text="{Binding DisplayText}" />
                </TextBlock>
                <TextBlock>
                    <Run Text="Position:" />
                    <Run FontWeight="SemiBold"
                         Text="{Binding Position}" />
                </TextBlock>

                <TextBlock>
                    <Run Text="Id:" />
                    <Run FontWeight="SemiBold"
                         Text="{Binding Id}" />
                </TextBlock>
            </StackPanel>
        </DataTemplate>

        <local:SuggestionTemplateSelector x:Key="SuggestionTemplateSelector"
                                          Data="{StaticResource DataTemplate}"
                                          Person="{StaticResource EmailTemplate}" />

    </Page.Resources>
    <StackPanel MinWidth="400"
                HorizontalAlignment="Center"
                Spacing="24">
        <controls:RichSuggestBox x:Name="SuggestingBox"
                                 HorizontalAlignment="Stretch"
                                 Header="RichSuggestBox that supports multiple prefixes (@ and #)"
                                 ItemTemplateSelector="{StaticResource SuggestionTemplateSelector}"
                                 Prefixes="@#"
                                 SuggestionChosen="SuggestingBox_SuggestionChosen"
                                 SuggestionRequested="SuggestingBox_SuggestionRequested" />
        <ListView x:Name="TokenListView"
                  HorizontalAlignment="Stretch"
                  ItemTemplate="{StaticResource TokenTemplate}" />
    </StackPanel>
</Page>
C#
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using CommunityToolkit.WinUI.Controls;

#if WINAPPSDK
using Microsoft.UI;
using Microsoft.UI.Text;
#else
using Windows.UI;
using Windows.UI.Text;
#endif

namespace RichSuggestBoxExperiment.Samples;

[ToolkitSample(id: nameof(RichSuggestBoxMultiplePrefixesSample), "RichSuggestBox with multiple prefixes", description: $"A sample for showing how to create and use a {nameof(RichSuggestBox)} with multiple prefixes.")]
public sealed partial class RichSuggestBoxMultiplePrefixesSample : Page
{
    private readonly List<SampleEmailDataType> _emailSamples = new List<SampleEmailDataType>()
        {
            new SampleEmailDataType() { FirstName = "Marcus", FamilyName = "Perryman" },
            new SampleEmailDataType() { FirstName = "Michael", FamilyName = "Hawker" },
            new SampleEmailDataType() { FirstName = "Matt", FamilyName = "Lacey" },
            new SampleEmailDataType() { FirstName = "Alexandre", FamilyName = "Chohfi" },
            new SampleEmailDataType() { FirstName = "Filip", FamilyName = "Wallberg" },
            new SampleEmailDataType() { FirstName = "Shane", FamilyName = "Weaver" },
            new SampleEmailDataType() { FirstName = "Vincent", FamilyName = "Gromfeld" },
            new SampleEmailDataType() { FirstName = "Sergio", FamilyName = "Pedri" },
            new SampleEmailDataType() { FirstName = "Alex", FamilyName = "Wilber" },
            new SampleEmailDataType() { FirstName = "Allan", FamilyName = "Deyoung" },
            new SampleEmailDataType() { FirstName = "Adele", FamilyName = "Vance" },
            new SampleEmailDataType() { FirstName = "Grady", FamilyName = "Archie" },
            new SampleEmailDataType() { FirstName = "Megan", FamilyName = "Bowen" },
            new SampleEmailDataType() { FirstName = "Ben", FamilyName = "Walters" },
            new SampleEmailDataType() { FirstName = "Debra", FamilyName = "Berger" },
            new SampleEmailDataType() { FirstName = "Emily", FamilyName = "Braun" },
            new SampleEmailDataType() { FirstName = "Christine", FamilyName = "Cline" },
            new SampleEmailDataType() { FirstName = "Enrico", FamilyName = "Catteneo" },
            new SampleEmailDataType() { FirstName = "Davit", FamilyName = "Badalyan" },
            new SampleEmailDataType() { FirstName = "Diego", FamilyName = "Siciliani" },
            new SampleEmailDataType() { FirstName = "Raul", FamilyName = "Razo" },
            new SampleEmailDataType() { FirstName = "Miriam", FamilyName = "Graham" },
            new SampleEmailDataType() { FirstName = "Lynne", FamilyName = "Robbins" },
            new SampleEmailDataType() { FirstName = "Lydia", FamilyName = "Holloway" },
            new SampleEmailDataType() { FirstName = "Nestor", FamilyName = "Wilke" },
            new SampleEmailDataType() { FirstName = "Patti", FamilyName = "Fernandez" },
            new SampleEmailDataType() { FirstName = "Pradeep", FamilyName = "Gupta" },
            new SampleEmailDataType() { FirstName = "Joni", FamilyName = "Sherman" },
            new SampleEmailDataType() { FirstName = "Isaiah", FamilyName = "Langer" },
            new SampleEmailDataType() { FirstName = "Irvin", FamilyName = "Sayers" },
            new SampleEmailDataType() { FirstName = "Tung", FamilyName = "Huynh" },
        };

    private readonly List<SampleDataType> _samples = new List<SampleDataType>()
        {
            new SampleDataType() { Text = "Account", Icon = Symbol.Account },
            new SampleDataType() { Text = "Add Friend", Icon = Symbol.AddFriend },
            new SampleDataType() { Text = "Attach", Icon = Symbol.Attach },
            new SampleDataType() { Text = "Attach Camera", Icon = Symbol.AttachCamera },
            new SampleDataType() { Text = "Audio", Icon = Symbol.Audio },
            new SampleDataType() { Text = "Block Contact", Icon = Symbol.BlockContact },
            new SampleDataType() { Text = "Calculator", Icon = Symbol.Calculator },
            new SampleDataType() { Text = "Calendar", Icon = Symbol.Calendar },
            new SampleDataType() { Text = "Camera", Icon = Symbol.Camera },
            new SampleDataType() { Text = "Contact", Icon = Symbol.Contact },
            new SampleDataType() { Text = "Favorite", Icon = Symbol.Favorite },
            new SampleDataType() { Text = "Link", Icon = Symbol.Link },
            new SampleDataType() { Text = "Mail", Icon = Symbol.Mail },
            new SampleDataType() { Text = "Map", Icon = Symbol.Map },
            new SampleDataType() { Text = "Phone", Icon = Symbol.Phone },
            new SampleDataType() { Text = "Pin", Icon = Symbol.Pin },
            new SampleDataType() { Text = "Rotate", Icon = Symbol.Rotate },
            new SampleDataType() { Text = "Rotate Camera", Icon = Symbol.RotateCamera },
            new SampleDataType() { Text = "Send", Icon = Symbol.Send },
            new SampleDataType() { Text = "Tags", Icon = Symbol.Tag },
            new SampleDataType() { Text = "UnFavorite", Icon = Symbol.UnFavorite },
            new SampleDataType() { Text = "UnPin", Icon = Symbol.UnPin },
            new SampleDataType() { Text = "Zoom", Icon = Symbol.Zoom },
            new SampleDataType() { Text = "ZoomIn", Icon = Symbol.ZoomIn },
            new SampleDataType() { Text = "ZoomOut", Icon = Symbol.ZoomOut },
        };
    public RichSuggestBoxMultiplePrefixesSample()
    {
        this.InitializeComponent();
        TokenListView.ItemsSource = SuggestingBox.Tokens;
    }

    private void SuggestingBox_SuggestionChosen(RichSuggestBox sender, SuggestionChosenEventArgs args)
    {
        if (args.Prefix == "#")
        {
            args.Format!.BackgroundColor = Colors.LightSlateGray;
            args.Format.ForegroundColor = Colors.White;
            args.Format.Bold = FormatEffect.On;
            args.DisplayText = ((SampleDataType)args.SelectedItem!).Text;
        }
        else
        {
            args.DisplayText = ((SampleEmailDataType)args.SelectedItem!).DisplayName;
        }
    }

    private void SuggestingBox_SuggestionRequested(RichSuggestBox sender, SuggestionRequestedEventArgs args)
    {
        if (args.Prefix == "#")
        {
            sender.ItemsSource = this._samples.Where(x => x.Text.Contains(args.QueryText!, StringComparison.OrdinalIgnoreCase));
        }
        else
        {
            sender.ItemsSource = this._emailSamples.Where(x => x.DisplayName.Contains(args.QueryText!, StringComparison.OrdinalIgnoreCase));
        }
    }
}

Syntaxe

XAML
<controls:RichSuggestBox
  PlaceholderText="Leave a comment"
  ItemTemplate="{StaticResource SuggestionTemplate}"
  Prefixes="@#" />

Notes

Lorsqu’une suggestion est sélectionnée, RichSuggestBox attribue à l’élément sélectionné un guid unique Guid et un texte d’affichage (fourni par le développeur) pour constituer un jeton. Le texte d’affichage est ensuite rempli avec des espaces de largeur zéro (ZWSP) et inséré dans le document sous forme de lien hypertexte en utilisant l'identifiant comme adresse de lien. Ces liens hypertexte sont suivis et validés sur chaque modification de texte.

Le texte du jeton inséré dans le document a la disposition suivante : ZWSP – Caractère préfixe – Texte d’affichage – ZWSP.

Par exemple, un jeton avec « @ » comme préfixe et « John Doe » comme texte d’affichage est inséré comme suit :

cs
"\u200b@John Doe\u200b"

Important

Le texte du jeton contient des espaces de largeur zéro, qui sont des caractères Unicode.

Note

Pour prendre en charge la fonction Annuler/Rétablir, RichSuggestBox conserve tous les jetons dans une collection interne même lorsque le texte du jeton est supprimé du document. Ces jetons sont marqués comme inactifs et ne sont pas inclus dans la collection Tokens. Utilisez la méthode ClearUndoRedoSuggestionHistory() pour effacer les jetons inactifs ou la méthode Clear() pour effacer tous les jetons.

Exemples

Gérer plusieurs types de jetons

L’exemple ci-dessous crée un RichSuggestBox qui peut tokeniser à la fois les mentions (la requête commence par @) et les hashtags (la requête commence par #).

XAML
<controls:RichSuggestBox
  PlaceholderText="Leave a comment"
  ItemTemplate="{StaticResource SuggestionTemplate}"
  SuggestionChosen="OnSuggestionChosen"
  SuggestionRequested="OnSuggestionRequested"
  Prefixes="@#" />
cs
private void OnSuggestionChosen(RichSuggestBox sender, SuggestionChosenEventArgs args)
{
  if (args.Prefix == "#")
  {
    // User selected a hashtag item
    args.DisplayText = ((SampleHashtagDataType)args.SelectedItem).Text;
  }
  else
  {
    // User selected a mention item
    args.DisplayText = ((SampleEmailDataType)args.SelectedItem).DisplayName;
  }
}

private void OnSuggestionRequested(RichSuggestBox sender, SuggestionRequestedEventArgs args)
{
  sender.ItemsSource = args.Prefix == "#"
    ? _hashtags.Where(x => x.Text.Contains(args.QueryText, StringComparison.OrdinalIgnoreCase))
    : _emails.Where(x => x.DisplayName.Contains(args.QueryText, StringComparison.OrdinalIgnoreCase));
}

Texte brut uniquement

L’exemple ci-dessous crée un RichSuggestBox qui n'autorise que la saisie de texte brut. Les seuls textes mis en forme dans le document sont des jetons.

XAML
<controls:RichSuggestBox
  ClipboardCopyFormat="PlainText"
  ClipboardPasteFormat="PlainText"
  DisabledFormattingAccelerators="All" />
XAML
<!--  Licensed to the .NET Foundation under one or more agreements. The .NET Foundation licenses this file to you under the MIT license. See the LICENSE file in the project root for more information.  -->
<Page x:Class="RichSuggestBoxExperiment.Samples.RichSuggestBoxPlainTextSample"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:controls="using:CommunityToolkit.WinUI.Controls"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:local="using:RichSuggestBoxExperiment.Samples"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d">
    <Page.Resources>
        <DataTemplate x:Key="EmailTemplate">
            <StackPanel Orientation="Horizontal">
                <Border Width="20"
                        Height="20"
                        Background="{ThemeResource AccentFillColorDefaultBrush}"
                        CornerRadius="9999">
                    <TextBlock HorizontalAlignment="Center"
                               VerticalAlignment="Center"
                               FontSize="10"
                               FontWeight="Semibold"
                               Foreground="White"
                               Text="{Binding Initials}" />
                </Border>
                <TextBlock Padding="8,0,0,0"
                           Text="{Binding DisplayName}" />
            </StackPanel>
        </DataTemplate>

        <DataTemplate x:Key="TokenTemplate">
            <StackPanel Margin="0,4,0,12"
                        Orientation="Vertical">
                <TextBlock>
                    <Run Text="Text:" />
                    <Run FontWeight="SemiBold"
                         Text="{Binding DisplayText}" />
                </TextBlock>
                <TextBlock>
                    <Run Text="Position:" />
                    <Run FontWeight="SemiBold"
                         Text="{Binding Position}" />
                </TextBlock>

                <TextBlock>
                    <Run Text="Id:" />
                    <Run FontWeight="SemiBold"
                         Text="{Binding Id}" />
                </TextBlock>
            </StackPanel>
        </DataTemplate>

        <local:SuggestionTemplateSelector x:Key="SuggestionTemplateSelector"
                                          Data="{StaticResource DataTemplate}"
                                          Person="{StaticResource EmailTemplate}" />

        <Flyout x:Key="TokenSelectedFlyout">
            <ContentPresenter x:Name="FlyoutPresenter"
                              ContentTemplate="{StaticResource EmailTemplate}" />
        </Flyout>

    </Page.Resources>
    <StackPanel MinWidth="400"
                HorizontalAlignment="Center"
                Spacing="24">

        <controls:RichSuggestBox x:Name="SuggestingBox"
                                 MaxHeight="400"
                                 HorizontalAlignment="Stretch"
                                 ClipboardCopyFormat="PlainText"
                                 ClipboardPasteFormat="PlainText"
                                 DisabledFormattingAccelerators="All"
                                 FlyoutBase.AttachedFlyout="{StaticResource TokenSelectedFlyout}"
                                 Header="Plain text RichSuggestBox with on token pointer over flyout"
                                 ItemTemplate="{StaticResource EmailTemplate}"
                                 Prefixes="@"
                                 SuggestionChosen="SuggestingBox_SuggestionChosen"
                                 SuggestionRequested="SuggestingBox_SuggestionRequested"
                                 TokenPointerOver="SuggestingBox_TokenPointerOver" />


        <ListView x:Name="TokenListView"
                  HorizontalAlignment="Stretch"
                  ItemTemplate="{StaticResource TokenTemplate}" />
    </StackPanel>
</Page>
C#
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using CommunityToolkit.WinUI.Controls;

#if WINAPPSDK
using Microsoft.UI;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Text;
#else
using Windows.UI;
using Windows.System;
using Windows.UI.Text;
#endif

namespace RichSuggestBoxExperiment.Samples;

[ToolkitSample(id: nameof(RichSuggestBoxPlainTextSample), "RichSuggestBox with plain text", description: $"A sample for showing how to create and use a {nameof(RichSuggestBox)} with plain text.")]
public sealed partial class RichSuggestBoxPlainTextSample : Page
{
    private DispatcherQueue _dispatcherQueue;
    private readonly List<SampleEmailDataType> _emailSamples = new List<SampleEmailDataType>()
        {
            new SampleEmailDataType() { FirstName = "Marcus", FamilyName = "Perryman" },
            new SampleEmailDataType() { FirstName = "Michael", FamilyName = "Hawker" },
            new SampleEmailDataType() { FirstName = "Matt", FamilyName = "Lacey" },
            new SampleEmailDataType() { FirstName = "Alexandre", FamilyName = "Chohfi" },
            new SampleEmailDataType() { FirstName = "Filip", FamilyName = "Wallberg" },
            new SampleEmailDataType() { FirstName = "Shane", FamilyName = "Weaver" },
            new SampleEmailDataType() { FirstName = "Vincent", FamilyName = "Gromfeld" },
            new SampleEmailDataType() { FirstName = "Sergio", FamilyName = "Pedri" },
            new SampleEmailDataType() { FirstName = "Alex", FamilyName = "Wilber" },
            new SampleEmailDataType() { FirstName = "Allan", FamilyName = "Deyoung" },
            new SampleEmailDataType() { FirstName = "Adele", FamilyName = "Vance" },
            new SampleEmailDataType() { FirstName = "Grady", FamilyName = "Archie" },
            new SampleEmailDataType() { FirstName = "Megan", FamilyName = "Bowen" },
            new SampleEmailDataType() { FirstName = "Ben", FamilyName = "Walters" },
            new SampleEmailDataType() { FirstName = "Debra", FamilyName = "Berger" },
            new SampleEmailDataType() { FirstName = "Emily", FamilyName = "Braun" },
            new SampleEmailDataType() { FirstName = "Christine", FamilyName = "Cline" },
            new SampleEmailDataType() { FirstName = "Enrico", FamilyName = "Catteneo" },
            new SampleEmailDataType() { FirstName = "Davit", FamilyName = "Badalyan" },
            new SampleEmailDataType() { FirstName = "Diego", FamilyName = "Siciliani" },
            new SampleEmailDataType() { FirstName = "Raul", FamilyName = "Razo" },
            new SampleEmailDataType() { FirstName = "Miriam", FamilyName = "Graham" },
            new SampleEmailDataType() { FirstName = "Lynne", FamilyName = "Robbins" },
            new SampleEmailDataType() { FirstName = "Lydia", FamilyName = "Holloway" },
            new SampleEmailDataType() { FirstName = "Nestor", FamilyName = "Wilke" },
            new SampleEmailDataType() { FirstName = "Patti", FamilyName = "Fernandez" },
            new SampleEmailDataType() { FirstName = "Pradeep", FamilyName = "Gupta" },
            new SampleEmailDataType() { FirstName = "Joni", FamilyName = "Sherman" },
            new SampleEmailDataType() { FirstName = "Isaiah", FamilyName = "Langer" },
            new SampleEmailDataType() { FirstName = "Irvin", FamilyName = "Sayers" },
            new SampleEmailDataType() { FirstName = "Tung", FamilyName = "Huynh" },
        };

    public RichSuggestBoxPlainTextSample()
    {
        this.InitializeComponent();
        _dispatcherQueue = DispatcherQueue.GetForCurrentThread();
        TokenListView.ItemsSource = SuggestingBox.Tokens;
    }

    private void SuggestingBox_SuggestionChosen(RichSuggestBox sender, SuggestionChosenEventArgs args)
    {
            args.DisplayText = ((SampleEmailDataType)args.SelectedItem!).DisplayName;
    }

    private void SuggestingBox_SuggestionRequested(RichSuggestBox sender, SuggestionRequestedEventArgs args)
    {
            sender.ItemsSource = this._emailSamples.Where(x => x.DisplayName.Contains(args.QueryText!, StringComparison.OrdinalIgnoreCase));
    }

    private void SuggestingBox_TokenPointerOver(RichSuggestBox sender, RichSuggestTokenPointerOverEventArgs args)
    {
        var flyout = (Flyout)FlyoutBase.GetAttachedFlyout(sender);
        var pointerPosition = args.CurrentPoint!.Position;

        if (flyout?.Content is ContentPresenter cp && sender.TextDocument!.Selection.Type != SelectionType.Normal &&
            (!flyout.IsOpen || cp.Content != args.Token!.Item))
        {
            this._dispatcherQueue.TryEnqueue(() =>
            {
                cp.Content = args.Token!.Item;
                flyout.ShowAt(sender, new FlyoutShowOptions
                {
                    Position = pointerPosition,
                    ExclusionRect = sender.GetRectFromRange(args.Range!),
                    ShowMode = FlyoutShowMode.TransientWithDismissOnPointerMoveAway,
                });
            });
        }
    }
}