Ajouter un élément InkToolbar à une application Windows

Il existe deux contrôles différents qui facilitent l'entrée manuscrite dans les applications Windows : InkCanvas et InkToolbar.

Le contrôle InkCanvas fournit des fonctionnalités Windows Ink de base. Utilisez-la pour restituer l’entrée de stylet sous la forme d’un trait d’encre (à l’aide de paramètres par défaut pour la couleur et l’épaisseur) ou d’un trait d’effacement.

Pour plus d’informations sur l’implémentation d’InkCanvas, consultez les interactions stylet et stylet dans les applications Windows.

En tant que superposition complètement transparente, InkCanvas ne fournit aucune interface utilisateur intégrée pour la définition des propriétés de trait d’encre. Si vous souhaitez modifier l’expérience d’entrée manuscrite par défaut, laissez les utilisateurs définir des propriétés de trait d’encre et prendre en charge d’autres fonctionnalités d’entrée manuscrite personnalisées, vous avez deux options :

  • Dans code-behind, utilisez l’objet InkPresenter sous-jacent lié à InkCanvas.

    Les API InkPresenter prennent en charge une personnalisation étendue de l’expérience d’entrée manuscrite. Pour plus de détails, consultez Interactions avec le stylo et le stylet dans les applications Windows.

  • Lier un InkToolbar à InkCanvas. Par défaut, InkToolbar fournit une collection personnalisable et extensible de boutons permettant d’activer des fonctionnalités liées aux entrées manuscrites telles que la taille du trait, la couleur d’encre et la pointe du stylet.

    Nous abordons la barre InkToolbar dans cette rubrique.

API importantes : classe InkCanvas, classe InkToolbar, classe InkPresenter, Windows.UI.Input.Inking

InkToolbar par défaut

Par défaut, la barre d'outils InkToolbar comprend des boutons permettant de dessiner, d'effacer, de mettre en évidence et d'afficher un pochoir (règle ou rapporteur). Selon la fonctionnalité, d’autres paramètres et commandes tels que la couleur de l’encre, l’épaisseur du trait, la suppression totale, sont fournis dans un menu volant.

InkToolbar
Barre d’outils Windows Ink par défaut

Pour ajouter un InkToolbar par défaut à une application d’entrée manuscrite, placez-le simplement sur la même page que votre InkCanvas et associez les deux contrôles.

  1. Dans MainPage.xaml, déclarez un objet conteneur (pour cet exemple, nous utilisons un contrôle Grid) pour l’aire d’entrée manuscrite.
  2. Déclarez un objet InkCanvas en tant qu’enfant du conteneur. (La taille InkCanvas est héritée du conteneur.)
  3. Déclarez un InkToolbar et utilisez l’attribut TargetInkCanvas pour le lier à InkCanvas.

Remarque

Vérifiez que InkToolbar est déclaré après InkCanvas. Si ce n’est pas le cas, la superposition InkCanvas restitue l’objet InkToolbar inaccessible.

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <StackPanel x:Name="HeaderPanel" Orientation="Horizontal" Grid.Row="0">
        <TextBlock x:Name="Header"
                   Text="Basic ink sample"
                   Style="{ThemeResource HeaderTextBlockStyle}"
                   Margin="10,0,0,0" />
    </StackPanel>
    <Grid Grid.Row="1">
        <Image Source="Assets\StoreLogo.png" />
        <InkCanvas x:Name="inkCanvas" />
        <InkToolbar x:Name="inkToolbar"
          VerticalAlignment="Top"
          TargetInkCanvas="{x:Bind inkCanvas}" />
    </Grid>
</Grid>

Personnalisations de base

Dans cette section, nous abordons certains scénarios de personnalisation de la barre d’outils Windows Ink de base.

Spécifier l’emplacement et l’orientation

Lorsque vous ajoutez une barre d’outils manuscrite à votre application, vous pouvez accepter l’emplacement et l’orientation par défaut de la barre d’outils ou les définir comme requis par votre application ou votre utilisateur.

XAML

Spécifiez explicitement l’emplacement et l’orientation de la barre d’outils via ses propriétés VerticalAlignment, HorizontalAlignment et Orientation.

Par défaut Explicite
Emplacement et orientation de la barre d’outils d’entrée manuscrite par défaut Emplacement et orientation explicites de la barre d’outils d’entrée manuscrite
Emplacement et orientation par défaut de la barre d’outils Windows Ink Emplacement et orientation explicites de la barre d’outils Windows Ink

Voici le code permettant de définir explicitement l’emplacement et l’orientation de la barre d’outils d’entrée manuscrite en XAML.

<InkToolbar x:Name="inkToolbar" 
    VerticalAlignment="Center" 
    HorizontalAlignment="Right" 
    Orientation="Vertical" 
    TargetInkCanvas="{x:Bind inkCanvas}" />

Initialiser en fonction des préférences utilisateur ou de l’état de l’appareil

Dans certains cas, vous pouvez définir l’emplacement et l’orientation de la barre d’outils d’entrée manuscrite en fonction de la préférence utilisateur ou de l’état de l’appareil. L’exemple suivant montre comment définir l’emplacement et l’orientation de la barre d’outils d’entrée manuscrite en fonction des préférences d’écriture de gauche ou de droite spécifiées via Paramètres > Appareils > stylo et Windows Ink > Stylo > Choisir la main avec laquelle vous écrivez.

Paramètre de main dominante
Paramètre de main dominante

Vous pouvez interroger ce paramètre via la propriété HandPreference de Windows.UI.ViewManagement et définir HorizontalAlignment en fonction de la valeur retournée. Dans cet exemple, nous localisons la barre d’outils sur le côté gauche de l’application pour une personne de gauche et sur le côté droit d’une personne de droite.

Téléchargez cet exemple à partir de l’emplacement et de l’orientation de la barre d’outils Ink (de base)

public MainPage()
{
    this.InitializeComponent();

    Windows.UI.ViewManagement.UISettings settings = 
        new Windows.UI.ViewManagement.UISettings();
    HorizontalAlignment alignment = 
        (settings.HandPreference == 
            Windows.UI.ViewManagement.HandPreference.LeftHanded) ? 
            HorizontalAlignment.Left : HorizontalAlignment.Right;
    inkToolbar.HorizontalAlignment = alignment;
}

Ajuster dynamiquement à l’état de l’utilisateur ou de l’appareil

Vous pouvez également utiliser la liaison pour rechercher les mises à jour de l’interface utilisateur en fonction des modifications apportées aux préférences utilisateur, aux paramètres de l’appareil ou aux états de l’appareil. Dans l’exemple suivant, nous développons l’exemple précédent et montrons comment positionner dynamiquement la barre d’outils d’entrée manuscrite en fonction de l’orientation de l’appareil à l’aide de la liaison, d’un objet ViewMOdel et de l’interface INotifyPropertyChanged.

Téléchargez cet exemple à partir de l’emplacement et de l’orientation de la barre d’outils Ink (dynamique)

  1. Tout d’abord, nous allons ajouter notre ViewModel.

    1. Ajoutez un nouveau dossier à votre projet et appelez-le ViewModels.

    2. Ajoutez une nouvelle classe au dossier ViewModels (pour cet exemple, nous l’avons appelée InkToolbarSnippetHostViewModel.cs).

      Remarque

      Nous avons utilisé le modèle Singleton comme nous n’avons besoin que d’un seul objet de ce type pour la durée de vie de l’application

    3. Ajoutez using System.ComponentModel espace de noms au fichier.

    4. Ajoutez une variable membre statique appelée instance et une propriété statique en lecture seule nommée Instance. Rendez le constructeur privé pour vous assurer que cette classe est accessible uniquement via la propriété Instance.

      Remarque

      Cette classe hérite de l’interface INotifyPropertyChanged, utilisée pour notifier les clients, généralement les clients de liaison, qu’une valeur de propriété a changé. Nous allons l’utiliser pour gérer les modifications apportées à l’orientation de l’appareil (nous allons développer ce code et expliquer plus loin dans une étape ultérieure).

      using System.ComponentModel;
      
      namespace locationandorientation.ViewModels
      {
          public class InkToolbarSnippetHostViewModel : INotifyPropertyChanged
          {
              private static InkToolbarSnippetHostViewModel instance;
      
              public static InkToolbarSnippetHostViewModel Instance
              {
                  get
                  {
                      if (null == instance)
                      {
                          instance = new InkToolbarSnippetHostViewModel();
                      }
                      return instance;
                  }
              }
          }
      
          private InkToolbarSnippetHostViewModel() { }
      }
      
    5. Ajoutez deux propriétés bool à la classe InkToolbarSnippetHostViewModel : LeftHandedLayout (même fonctionnalité que l’exemple XAML précédent) et PortraitLayout (orientation de l’appareil).

      Remarque

      La propriété PortraitLayout est settable et inclut la définition de l’événement PropertyChanged .

      public bool LeftHandedLayout
      {
          get
          {
              bool leftHandedLayout = false;
              Windows.UI.ViewManagement.UISettings settings =
                  new Windows.UI.ViewManagement.UISettings();
              leftHandedLayout = (settings.HandPreference ==
                  Windows.UI.ViewManagement.HandPreference.LeftHanded);
              return leftHandedLayout;
          }
      }
      
      public bool portraitLayout = false;
      public bool PortraitLayout
      {
          get
          {
              Windows.UI.ViewManagement.ApplicationViewOrientation winOrientation = 
                  Windows.UI.ViewManagement.ApplicationView.GetForCurrentView().Orientation;
              portraitLayout = 
                  (winOrientation == 
                      Windows.UI.ViewManagement.ApplicationViewOrientation.Portrait);
              return portraitLayout;
          }
          set
          {
              if (value.Equals(portraitLayout)) return;
              portraitLayout = value;
              PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("PortraitLayout"));
          }
      }
      
  2. Nous allons maintenant ajouter quelques classes de convertisseur à notre projet. Chaque classe contient un objet Convert qui retourne une valeur d’alignement ( HorizontalAlignment ou VerticalAlignment).

    1. Ajoutez un nouveau dossier à votre projet et appelez-le Convertisseurs.

    2. Ajoutez deux nouvelles classes au dossier Converters (pour cet exemple, nous les appelons HorizontalAlignmentFromHandednessConverter.cs et VerticalAlignmentFromAppViewConverter.cs).

    3. Ajoutez using Windows.UI.Xaml et using Windows.UI.Xaml.Data espaces de noms à chaque fichier.

    4. Modifiez chaque classe et public spécifiez qu’elle implémente l’interface IValueConverter.

    5. Ajoutez les méthodes Convert et ConvertBack à chaque fichier, comme illustré ici (nous laissons la méthode ConvertBack non implémentée).

      • HorizontalAlignmentFromHandednessConverter positionne la barre d’outils manuscrite sur le côté droit de l’application pour les utilisateurs droitiers et à gauche de l’application pour les utilisateurs gauchers.
      using System;
      
      using Windows.UI.Xaml;
      using Windows.UI.Xaml.Data;
      
      namespace locationandorientation.Converters
      {
          public class HorizontalAlignmentFromHandednessConverter : IValueConverter
          {
              public object Convert(object value, Type targetType,
                  object parameter, string language)
              {
                  bool leftHanded = (bool)value;
                  HorizontalAlignment alignment = HorizontalAlignment.Right;
                  if (leftHanded)
                  {
                      alignment = HorizontalAlignment.Left;
                  }
                  return alignment;
              }
      
              public object ConvertBack(object value, Type targetType,
                  object parameter, string language)
              {
                  throw new NotImplementedException();
              }
          }
      }
      
      • VerticalAlignmentFromAppViewConverter positionne la barre d’outils d’entrée manuscrite au centre de l’application pour l’orientation portrait et en haut de l’application pour l’orientation paysage (tout en vue d’améliorer la facilité d’utilisation, il s’agit simplement d’un choix arbitraire à des fins de démonstration).
      using System;
      
      using Windows.UI.Xaml;
      using Windows.UI.Xaml.Data;
      
      namespace locationandorientation.Converters
      {
          public class VerticalAlignmentFromAppViewConverter : IValueConverter
          {
              public object Convert(object value, Type targetType,
                  object parameter, string language)
              {
                  bool portraitOrientation = (bool)value;
                  VerticalAlignment alignment = VerticalAlignment.Top;
                  if (portraitOrientation)
                  {
                      alignment = VerticalAlignment.Center;
                  }
                  return alignment;
              }
      
              public object ConvertBack(object value, Type targetType,
                  object parameter, string language)
              {
                  throw new NotImplementedException();
              }
          }
      }
      
  3. Ouvrez le fichier MainPage.xaml.cs.

    1. Ajoutez using using locationandorientation.ViewModels à la liste des espaces de noms pour associer notre ViewModel.
    2. Ajoutez using Windows.UI.ViewManagement à la liste des espaces de noms pour permettre l’écoute des modifications apportées à l’orientation de l’appareil.
    3. Ajoutez le code WindowSizeChangedEventHandler.
    4. Définissez DataContext pour la vue sur l’instance singleton de la classe InkToolbarSnippetHostViewModel.
    using Windows.UI.Xaml;
    using Windows.UI.Xaml.Controls;
    
    using locationandorientation.ViewModels;
    using Windows.UI.ViewManagement;
    
    namespace locationandorientation
    {
        public sealed partial class MainPage : Page
        {
            public MainPage()
            {
                this.InitializeComponent();
    
                Window.Current.SizeChanged += (sender, args) =>
                {
                    ApplicationView currentView = ApplicationView.GetForCurrentView();
    
                    if (currentView.Orientation == ApplicationViewOrientation.Landscape)
                    {
                        InkToolbarSnippetHostViewModel.Instance.PortraitLayout = false;
                    }
                    else if (currentView.Orientation == ApplicationViewOrientation.Portrait)
                    {
                        InkToolbarSnippetHostViewModel.Instance.PortraitLayout = true;
                    }
                };
    
                DataContext = InkToolbarSnippetHostViewModel.Instance;
            }
        }
    }
    
  4. Ouvrez le fichier MainPage.xaml.

    1. Ajoutez xmlns:converters="using:locationandorientation.Converters" à l’élément Page pour la liaison à nos convertisseurs.

      <Page
      x:Class="locationandorientation.MainPage"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:locationandorientation"
      xmlns:converters="using:locationandorientation.Converters"
      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
      mc:Ignorable="d">
      
    2. Ajoutez un élément PageResources et spécifiez des références à nos convertisseurs.

      <Page.Resources>
          <converters:HorizontalAlignmentFromHandednessConverter x:Key="HorizontalAlignmentConverter"/>
          <converters:VerticalAlignmentFromAppViewConverter x:Key="VerticalAlignmentConverter"/>
      </Page.Resources>
      
    3. Ajoutez les éléments InkCanvas et InkToolbar et liez les propriétés VerticalAlignment et HorizontalAlignment de InkToolbar.

      <InkCanvas x:Name="inkCanvas" />
      <InkToolbar x:Name="inkToolbar" 
                  VerticalAlignment="{Binding PortraitLayout, Converter={StaticResource VerticalAlignmentConverter} }" 
                  HorizontalAlignment="{Binding LeftHandedLayout, Converter={StaticResource HorizontalAlignmentConverter} }" 
                  Orientation="Vertical" 
                  TargetInkCanvas="{x:Bind inkCanvas}" />
      
  5. Revenez au fichier InkToolbarSnippetHostViewModel.cs pour ajouter nos PortraitLayout propriétés et LeftHandedLayout nos propriétés bool à la classe, ainsi que la prise en charge de la InkToolbarSnippetHostViewModel rebinding PortraitLayout lorsque cette valeur de propriété change.

    public bool LeftHandedLayout
    {
        get
        {
            bool leftHandedLayout = false;
            Windows.UI.ViewManagement.UISettings settings =
                new Windows.UI.ViewManagement.UISettings();
            leftHandedLayout = (settings.HandPreference ==
                Windows.UI.ViewManagement.HandPreference.LeftHanded);
            return leftHandedLayout;
        }
    }
    
    public bool portraitLayout = false;
    public bool PortraitLayout
    {
        get
        {
            Windows.UI.ViewManagement.ApplicationViewOrientation winOrientation = 
                Windows.UI.ViewManagement.ApplicationView.GetForCurrentView().Orientation;
            portraitLayout = 
                (winOrientation == 
                    Windows.UI.ViewManagement.ApplicationViewOrientation.Portrait);
            return portraitLayout;
        }
        set
        {
            if (value.Equals(portraitLayout)) return;
            portraitLayout = value;
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("PortraitLayout"));
        }
    }
    
    #region INotifyPropertyChanged Members
    
    public event PropertyChangedEventHandler PropertyChanged;
    
    protected void OnPropertyChanged(string property)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
    }
    
    #endregion
    

Vous devez maintenant disposer d’une application d’entrée manuscrite qui s’adapte à la préférence de main dominante de l’utilisateur et répond dynamiquement à l’orientation de l’appareil de l’utilisateur.

Spécifier le bouton sélectionné

Bouton crayon sélectionné lors de l’initialisation
Barre d’outils Windows Ink avec bouton crayon sélectionné lors de l’initialisation

Par défaut, le premier bouton (ou le plus à gauche) est sélectionné lorsque votre application est lancée et que la barre d’outils est initialisée. Dans la barre d’outils Windows Ink par défaut, il s’agit du bouton de stylet à bille.

Étant donné que l’infrastructure définit l’ordre des boutons intégrés, le premier bouton peut ne pas être le stylet ou l’outil que vous souhaitez activer par défaut.

Vous pouvez remplacer ce comportement par défaut et spécifier le bouton sélectionné dans la barre d’outils.

Pour cet exemple, nous initialisons la barre d’outils par défaut avec le bouton crayon sélectionné et le crayon activé (au lieu du stylet de point de boule).

  1. Utilisez la déclaration XAML pour InkCanvas et InkToolbar de l’exemple précédent.
  2. Dans code-behind, configurez un gestionnaire pour l’événement Loaded de l’objet InkToolbar.
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// Here, we set up InkToolbar event listeners.
/// </summary>
public MainPage_CodeBehind()
{
    this.InitializeComponent();
    // Add handlers for InkToolbar events.
    inkToolbar.Loaded += inkToolbar_Loaded;
}
  1. Dans le gestionnaire de l’événement Loaded :

    1. Obtenez une référence à l’objet InkToolbarPencilButton intégré.

    Le passage d’un objet InkToolbarTool.Pencil dans la méthode GetToolButton renvoie un objet InkToolbarToolButton pour InkToolbarPencilButton.

    1. Définissez ActiveTool sur l’objet retourné à l’étape précédente.
/// <summary>
/// Handle the Loaded event of the InkToolbar.
/// By default, the active tool is set to the first tool on the toolbar.
/// Here, we set the active tool to the pencil button.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void inkToolbar_Loaded(object sender, RoutedEventArgs e)
{
    InkToolbarToolButton pencilButton = inkToolbar.GetToolButton(InkToolbarTool.Pencil);
    inkToolbar.ActiveTool = pencilButton;
}

Spécifier les boutons intégrés

Boutons spécifiques inclus lors de l’initialisation
Boutons spécifiques inclus lors de l’initialisation

Comme mentionné, la barre d’outils Windows Ink inclut une collection de boutons intégrés par défaut. Ces boutons sont affichés dans l’ordre suivant (de gauche à droite) :

Pour cet exemple, nous initialisons la barre d’outils avec uniquement le stylet, le crayon et les boutons gomme intégrés.

Pour ce faire, vous pouvez utiliser XAML ou code-behind.

XAML

Modifiez la déclaration XAML pour InkCanvas et InkToolbar à partir du premier exemple.

Remarque

Les boutons sont ajoutés à la barre d’outils dans l’ordre défini par le cadre, et non dans l’ordre spécifié ici.

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <StackPanel x:Name="HeaderPanel" Orientation="Horizontal" Grid.Row="0">
        <TextBlock x:Name="Header"
                   Text="Basic ink sample"
                   Style="{ThemeResource HeaderTextBlockStyle}"
                   Margin="10,0,0,0" />
    </StackPanel>
    <Grid Grid.Row="1">
        <Image Source="Assets\StoreLogo.png" />
        <!-- Clear the default InkToolbar buttons by setting InitialControls to None. -->
        <!-- Set the active tool to the pencil button. -->
        <InkCanvas x:Name="inkCanvas" />
        <InkToolbar x:Name="inkToolbar"
                    VerticalAlignment="Top"
                    TargetInkCanvas="{x:Bind inkCanvas}"
                    InitialControls="None">
            <!--
             Add only the ballpoint pen, pencil, and eraser.
             Note that the buttons are added to the toolbar in the order
             defined by the framework, not the order we specify here.
            -->
            <InkToolbarEraserButton />
            <InkToolbarBallpointPenButton />
            <InkToolbarPencilButton/>
        </InkToolbar>
    </Grid>
</Grid>

Code-behind

  1. Utilisez la déclaration XAML pour InkCanvas et InkToolbar à partir du premier exemple.
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <StackPanel x:Name="HeaderPanel" Orientation="Horizontal" Grid.Row="0">
        <TextBlock x:Name="Header"
                   Text="Basic ink sample"
                   Style="{ThemeResource HeaderTextBlockStyle}"
                   Margin="10,0,0,0" />
    </StackPanel>
    <Grid Grid.Row="1">
        <Image Source="Assets\StoreLogo.png" />
        <InkCanvas x:Name="inkCanvas" />
        <InkToolbar x:Name="inkToolbar"
        VerticalAlignment="Top"
        TargetInkCanvas="{x:Bind inkCanvas}" />
    </Grid>
</Grid>
  1. Dans code-behind, configurez un gestionnaire pour l’événement De chargement de l’objet InkToolbar.
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// Here, we set up InkToolbar event listeners.
/// </summary>
public MainPage_CodeBehind()
{
    this.InitializeComponent();
    // Add handlers for InkToolbar events.
    inkToolbar.Loading += inkToolbar_Loading;
}
  1. Définissez InitialControls sur « None ».
  2. Créez des références d’objet pour les boutons requis par votre application. Ici, nous ajoutons InkToolbarBallpointPenButton, InkToolbarPencilButton et InkToolbarEraserButton uniquement.

Remarque

Les boutons sont ajoutés à la barre d’outils dans l’ordre défini par le cadre, et non dans l’ordre spécifié ici.

  1. Ajoutez les boutons à InkToolbar.
/// <summary>
/// Handles the Loading event of the InkToolbar.
/// Here, we identify the buttons to include on the InkToolbar.
/// </summary>
/// <param name="sender">The InkToolbar</param>
/// <param name="args">The InkToolbar event data.
/// If there is no event data, this parameter is null</param>
private void inkToolbar_Loading(FrameworkElement sender, object args)
{
    // Clear all built-in buttons from the InkToolbar.
    inkToolbar.InitialControls = InkToolbarInitialControls.None;

    // Add only the ballpoint pen, pencil, and eraser.
    // Note that the buttons are added to the toolbar in the order
    // defined by the framework, not the order we specify here.
    InkToolbarBallpointPenButton ballpoint = new InkToolbarBallpointPenButton();
    InkToolbarPencilButton pencil = new InkToolbarPencilButton();
    InkToolbarEraserButton eraser = new InkToolbarEraserButton();
    inkToolbar.Children.Add(eraser);
    inkToolbar.Children.Add(ballpoint);
    inkToolbar.Children.Add(pencil);
}

Boutons personnalisés et fonctionnalités d’entrée manuscrite

Vous pouvez personnaliser et étendre la collection de boutons (et fonctionnalités d’entrée manuscrites associées) fournis via InkToolbar.

InkToolbar se compose de deux groupes distincts de types de boutons :

  1. Groupe de boutons « outil » contenant le dessin intégré, l’effacement et la mise en surbrillance des boutons. Les stylets et outils personnalisés sont ajoutés ici.

Notez que la sélection de fonctionnalités s’exclue mutuellement.

  1. Groupe de boutons « bascule » contenant le bouton de règle intégré. Les bascules personnalisées sont ajoutées ici.

Notez que les fonctionnalités ne sont pas mutuellement exclusives et peuvent être utilisées simultanément avec d’autres outils actifs.

Selon votre application et la fonctionnalité d’entrée manuscrite requises, vous pouvez ajouter l’un des boutons suivants (liés à vos fonctionnalités manuscrites personnalisées) à InkToolbar :

  • Stylet personnalisé : stylet pour lequel la palette de couleurs manuscrites et les propriétés de pointe du stylet, telles que la forme, la rotation et la taille, sont définies par l’application hôte.
  • Outil personnalisé : un outil non-stylet, défini par l’application hôte.
  • Bascule personnalisée : définit l’état d’une fonctionnalité définie par l’application sur activé ou désactivé. Quand elle est activée, la fonctionnalité fonctionne conjointement avec l’outil actif.

Notez que vous ne pouvez pas modifier l’ordre d’affichage des boutons intégrés. L’ordre d’affichage par défaut est le stylet de point de boule, le crayon, le surligneur, la gomme et la règle. Les stylets personnalisés sont ajoutés au dernier stylet par défaut, les boutons d’outil personnalisés sont ajoutés entre le dernier bouton de stylet et le bouton gomme et les boutons bascule personnalisés sont ajoutés après le bouton règle. (Les boutons personnalisés sont ajoutés dans l’ordre dans lequel ils sont spécifiés).

Stylet personnalisé

Vous pouvez créer un stylet personnalisé (activé par le biais d’un bouton de stylet personnalisé) où vous définissez la palette de couleurs manuscrites et les propriétés de pointe du stylet, telles que la forme, la rotation et la taille.

Bouton de stylet calligraphique personnalisé
Bouton de stylet calligraphique personnalisé

Pour cet exemple, nous définissons un stylet personnalisé avec une large pointe qui permet des traits d’encre manuscrites de base. Nous personnalisons également la collection de pinceaux dans la palette affichée dans le menu volant du bouton.

Code-behind

Tout d’abord, nous définissons notre stylet personnalisé et spécifions les attributs de dessin dans code-behind. Nous référencerons ce stylet personnalisé à partir de XAML ultérieurement.

  1. Dans l’Explorateur de solutions, cliquez avec le bouton droit sur le projet, puis sélectionnez Ajouter, > Nouvel élément.
  2. Sous Visual C# -> Code, ajoutez un nouveau fichier de classe et appelez-le CalligraphicPen.cs.
  3. Dans Calligraphic.cs, remplacez le bloc par défaut par le bloc suivant :
using System.Numerics;
using Windows.UI;
using Windows.UI.Input.Inking;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
  1. Spécifiez que la classe CalligraphicPen est dérivée d’InkToolbarCustomPen.
class CalligraphicPen : InkToolbarCustomPen
{
}
  1. Remplacez CreateInkDrawingAttributesCore pour spécifier votre propre pinceau et taille de trait.
class CalligraphicPen : InkToolbarCustomPen
{
    protected override InkDrawingAttributes
      CreateInkDrawingAttributesCore(Brush brush, double strokeWidth)
    {
    }
}
  1. Créez un objet InkDrawingAttributes et définissez la forme de la pointe du stylet, la rotation des pointes, la taille des traits et la couleur d’encre.
class CalligraphicPen : InkToolbarCustomPen
{
    protected override InkDrawingAttributes
      CreateInkDrawingAttributesCore(Brush brush, double strokeWidth)
    {
        InkDrawingAttributes inkDrawingAttributes =
          new InkDrawingAttributes();
        inkDrawingAttributes.PenTip = PenTipShape.Circle;
        inkDrawingAttributes.Size =
          new Windows.Foundation.Size(strokeWidth, strokeWidth * 20);
        SolidColorBrush solidColorBrush = brush as SolidColorBrush;
        if (solidColorBrush != null)
        {
            inkDrawingAttributes.Color = solidColorBrush.Color;
        }
        else
        {
            inkDrawingAttributes.Color = Colors.Black;
        }

        Matrix3x2 matrix = Matrix3x2.CreateRotation(45);
        inkDrawingAttributes.PenTipTransform = matrix;

        return inkDrawingAttributes;
    }
}

XAML

Ensuite, nous ajoutons les références nécessaires au stylet personnalisé dans MainPage.xaml.

  1. Nous déclarons un dictionnaire de ressources de page local qui crée une référence au stylet personnalisé (CalligraphicPen) défini dans CalligraphicPen.cs et une collection de pinceaus prise en charge par le stylet personnalisé (CalligraphicPenPalette).
<Page.Resources>
    <!-- Add the custom CalligraphicPen to the page resources. -->
    <local:CalligraphicPen x:Key="CalligraphicPen" />
    <!-- Specify the colors for the palette of the custom pen. -->
    <BrushCollection x:Key="CalligraphicPenPalette">
        <SolidColorBrush Color="Blue" />
        <SolidColorBrush Color="Red" />
    </BrushCollection>
</Page.Resources>
  1. Nous ajoutons ensuite une entrée InkToolbar avec un élément InkToolbarCustomPenButton enfant.

Le bouton de stylet personnalisé inclut les deux références de ressources statiques déclarées dans les ressources de page : CalligraphicPen et CalligraphicPenPalette.

Nous spécifions également la plage du curseur de taille de trait (MinStrokeWidth, MaxStrokeWidth et SelectedStrokeWidth), le pinceau sélectionné (SelectedBrushIndex) et l’icône du bouton de stylet personnalisé (SymbolIcon).

<Grid Grid.Row="1">
    <InkCanvas x:Name="inkCanvas" />
    <InkToolbar x:Name="inkToolbar"
                VerticalAlignment="Top"
                TargetInkCanvas="{x:Bind inkCanvas}">
        <InkToolbarCustomPenButton
            CustomPen="{StaticResource CalligraphicPen}"
            Palette="{StaticResource CalligraphicPenPalette}"
            MinStrokeWidth="1" MaxStrokeWidth="3" SelectedStrokeWidth="2"
            SelectedBrushIndex ="1">
            <SymbolIcon Symbol="Favorite" />
            <InkToolbarCustomPenButton.ConfigurationContent>
                <InkToolbarPenConfigurationControl />
            </InkToolbarCustomPenButton.ConfigurationContent>
        </InkToolbarCustomPenButton>
    </InkToolbar>
</Grid>

Bascule personnalisée

Vous pouvez créer un bouton bascule personnalisé (activé via un bouton bascule personnalisé) pour définir l’état d’une fonctionnalité définie par l’application sur activé ou désactivé. Quand elle est activée, la fonctionnalité fonctionne conjointement avec l’outil actif.

Dans cet exemple, nous définissons un bouton bascule personnalisé qui active l’entrée manuscrite avec l’entrée tactile (par défaut, l’entrée tactile n’est pas activée).

Remarque

Si vous devez prendre en charge l’entrée manuscrite avec l’interaction tactile, nous vous recommandons de l’activer à l’aide d’un CustomToggleButton, avec l’icône et l’info-bulle spécifiées dans cet exemple.

En règle générale, l’entrée tactile est utilisée pour la manipulation directe d’un objet ou de l’interface utilisateur de l’application. Pour illustrer les différences de comportement lorsque l’entrée manuscrite est activée, nous placez InkCanvas dans un conteneur ScrollViewer et définissons les dimensions de ScrollViewer comme étant plus petites que celles de InkCanvas.

Lorsque l’application démarre, seule l’entrée manuscrite est prise en charge et l’interaction tactile est utilisée pour faire défiler ou zoomer l’aire d’entrée manuscrite. Lorsque l’entrée tactile est activée, la surface d’entrée manuscrite ne peut pas être mise en panne ou zoomée via l’entrée tactile.

Remarque

Consultez les contrôles d’entrée manuscrite pour les instructions d’expérience utilisateur InkCanvas et InkToolbar. Les recommandations suivantes sont pertinentes pour cet exemple :

  • InkToolbar, et en général, l’entrée manuscrite est la meilleure expérience par le biais d’un stylet actif. Toutefois, l’entrée manuscrite avec la souris et la touche tactile peut être prise en charge si nécessaire par votre application.
  • En cas de prise en charge de l’entrée manuscrite avec la fonctionnalité tactile, nous recommandons d’utiliser l’icône ED5F de la police Segoe MLD2 Assets pour le bouton bascule, avec une info-bulle « écriture tactile ».

XAML

  1. Tout d’abord, nous déclarons un élément InkToolbarCustomToggleButton (toggleButton) avec un écouteur d’événements Click qui spécifie le gestionnaire d’événements (Toggle_Custom).
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>

    <StackPanel Grid.Row="0" 
                x:Name="HeaderPanel" 
                Orientation="Horizontal">
        <TextBlock x:Name="Header" 
                   Text="Basic ink sample" 
                   Style="{ThemeResource HeaderTextBlockStyle}" 
                   Margin="10" />
    </StackPanel>

    <ScrollViewer Grid.Row="1" 
                  HorizontalScrollBarVisibility="Auto" 
                  VerticalScrollBarVisibility="Auto">
        
        <Grid HorizontalAlignment="Left" VerticalAlignment="Top">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            
            <InkToolbar Grid.Row="0" 
                        Margin="10"
                        x:Name="inkToolbar" 
                        VerticalAlignment="Top"
                        TargetInkCanvas="{x:Bind inkCanvas}">
                <InkToolbarCustomToggleButton 
                x:Name="toggleButton" 
                Click="CustomToggle_Click" 
                ToolTipService.ToolTip="Touch Writing">
                    <SymbolIcon Symbol="{x:Bind TouchWritingIcon}"/>
                </InkToolbarCustomToggleButton>
            </InkToolbar>
            
            <ScrollViewer Grid.Row="1" 
                          Height="500"
                          Width="500"
                          x:Name="scrollViewer" 
                          ZoomMode="Enabled" 
                          MinZoomFactor=".1" 
                          VerticalScrollMode="Enabled" 
                          VerticalScrollBarVisibility="Auto" 
                          HorizontalScrollMode="Enabled" 
                          HorizontalScrollBarVisibility="Auto">
                
                <Grid x:Name="outputGrid" 
                      Height="1000"
                      Width="1000"
                      Background="{ThemeResource SystemControlBackgroundChromeWhiteBrush}">
                    <InkCanvas x:Name="inkCanvas"/>
                </Grid>
                
            </ScrollViewer>
        </Grid>
    </ScrollViewer>
</Grid>

Code-behind

  1. Dans l’extrait de code précédent, nous avons déclaré un écouteur d’événements Click et un gestionnaire (Toggle_Custom) sur le bouton bascule personnalisé pour l’entrée manuscrite tactile (toggleButton). Ce gestionnaire prend simplement en charge CoreInputDeviceTypes.Touch via la propriété InputDeviceTypes de InkPresenter.

    Nous avons également spécifié une icône pour le bouton à l’aide de l’élément SymbolIcon et de l’extension de balisage {x :Bind} qui la lie à un champ défini dans le fichier code-behind (TouchWritingIcon).

    L’extrait de code suivant inclut à la fois le gestionnaire d’événements Click et la définition de TouchWritingIcon.

namespace Ink_Basic_InkToolbar
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage_AddCustomToggle : Page
    {
        Symbol TouchWritingIcon = (Symbol)0xED5F;

        public MainPage_AddCustomToggle()
        {
            this.InitializeComponent();
        }

        // Handler for the custom toggle button that enables touch inking.
        private void CustomToggle_Click(object sender, RoutedEventArgs e)
        {
            if (toggleButton.IsChecked == true)
            {
                inkCanvas.InkPresenter.InputDeviceTypes |= CoreInputDeviceTypes.Touch;
            }
            else
            {
                inkCanvas.InkPresenter.InputDeviceTypes &= ~CoreInputDeviceTypes.Touch;
            }
        }
    }
}

Outil personnalisé

Vous pouvez créer un bouton d’outil personnalisé pour appeler un outil non-stylet défini par votre application.

Par défaut, un InkPresenter traite toutes les entrées en tant que trait d’encre ou trait d’effacement. Cela inclut l’entrée modifiée par une affordance matérielle secondaire telle qu’un bouton de canon de stylet, un bouton de souris droit ou similaire. Toutefois, InkPresenter peut être configuré pour laisser une entrée spécifique non traitée, qui peut ensuite être transmise à votre application pour un traitement personnalisé.

Dans cet exemple, nous définissons un bouton d’outil personnalisé qui, lorsqu’il est sélectionné, entraîne le traitement et le rendu des traits suivants sous la forme d’un lasso de sélection (trait en pointillés) au lieu d’encre. Tous les traits d’encre dans les limites de la zone de sélection sont définis sur Sélectionné.

Remarque

Consultez les contrôles d’entrée manuscrite pour les instructions d’expérience utilisateur InkCanvas et InkToolbar. La recommandation suivante est pertinente pour cet exemple :

  • Dans le cas d’une sélection du trait, nous recommandons d’utiliser l’icône EF20 de la police Segoe MLD2 Assets pour le bouton outil, avec une info-bulle « outil de sélection ».

XAML

  1. Tout d’abord, nous déclarons un élément InkToolbarCustomToolButton (customToolButton) avec un écouteur d’événements Click qui spécifie le gestionnaire d’événements (customToolButton_Click) où la sélection de traits est configurée. (Nous avons également ajouté un ensemble de boutons pour copier, couper et coller la sélection du trait.)

  2. Nous ajoutons également un élément Canvas pour dessiner notre trait de sélection. L’utilisation d’une couche distincte pour dessiner le trait de sélection garantit que InkCanvas et son contenu restent intacts.

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <StackPanel x:Name="HeaderPanel" Orientation="Horizontal" Grid.Row="0">
        <TextBlock x:Name="Header" 
                   Text="Basic ink sample" 
                   Style="{ThemeResource HeaderTextBlockStyle}" 
                   Margin="10,0,0,0" />
    </StackPanel>
    <StackPanel x:Name="ToolPanel" Orientation="Horizontal" Grid.Row="1">
        <InkToolbar x:Name="inkToolbar" 
                    VerticalAlignment="Top" 
                    TargetInkCanvas="{x:Bind inkCanvas}">
            <InkToolbarCustomToolButton 
                x:Name="customToolButton" 
                Click="customToolButton_Click" 
                ToolTipService.ToolTip="Selection tool">
                <SymbolIcon Symbol="{x:Bind SelectIcon}"/>
            </InkToolbarCustomToolButton>
        </InkToolbar>
        <Button x:Name="cutButton" 
                Content="Cut" 
                Click="cutButton_Click"
                Width="100"
                Margin="5,0,0,0"/>
        <Button x:Name="copyButton" 
                Content="Copy"  
                Click="copyButton_Click"
                Width="100"
                Margin="5,0,0,0"/>
        <Button x:Name="pasteButton" 
                Content="Paste"  
                Click="pasteButton_Click"
                Width="100"
                Margin="5,0,0,0"/>
    </StackPanel>
    <Grid Grid.Row="2" x:Name="outputGrid" 
              Background="{ThemeResource SystemControlBackgroundChromeWhiteBrush}" 
              Height="Auto">
        <!-- Canvas for displaying selection UI. -->
        <Canvas x:Name="selectionCanvas"/>
        <!-- Canvas for displaying ink. -->
        <InkCanvas x:Name="inkCanvas" />
    </Grid>
</Grid>

Code-behind

  1. Nous gérons ensuite l’événement Click pour InkToolbarCustomToolButton dans le fichier code-behind MainPage.xaml.cs.

    Ce gestionnaire configure InkPresenter pour passer une entrée non traitées à l’application.

    Pour obtenir une étape plus détaillée de ce code : consultez l’entrée directe pour la section de traitement avancé des interactions stylet et Windows Ink dans les applications Windows.

    Nous avons également spécifié une icône pour le bouton à l’aide de l’élément SymbolIcon et de l’extension de balisage {x :Bind} qui la lie à un champ défini dans le fichier code-behind (SelectIcon).

    L’extrait de code suivant inclut à la fois le gestionnaire d’événements Click et la définition de SelectIcon.

namespace Ink_Basic_InkToolbar
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage_AddCustomTool : Page
    {
        // Icon for custom selection tool button.
        Symbol SelectIcon = (Symbol)0xEF20;

        // Stroke selection tool.
        private Polyline lasso;
        // Stroke selection area.
        private Rect boundingRect;

        public MainPage_AddCustomTool()
        {
            this.InitializeComponent();

            // Listen for new ink or erase strokes to clean up selection UI.
            inkCanvas.InkPresenter.StrokeInput.StrokeStarted +=
                StrokeInput_StrokeStarted;
            inkCanvas.InkPresenter.StrokesErased +=
                InkPresenter_StrokesErased;
        }

        private void customToolButton_Click(object sender, RoutedEventArgs e)
        {
            // By default, the InkPresenter processes input modified by 
            // a secondary affordance (pen barrel button, right mouse 
            // button, or similar) as ink.
            // To pass through modified input to the app for custom processing 
            // on the app UI thread instead of the background ink thread, set 
            // InputProcessingConfiguration.RightDragAction to LeaveUnprocessed.
            inkCanvas.InkPresenter.InputProcessingConfiguration.RightDragAction =
                InkInputRightDragAction.LeaveUnprocessed;

            // Listen for unprocessed pointer events from modified input.
            // The input is used to provide selection functionality.
            inkCanvas.InkPresenter.UnprocessedInput.PointerPressed +=
                UnprocessedInput_PointerPressed;
            inkCanvas.InkPresenter.UnprocessedInput.PointerMoved +=
                UnprocessedInput_PointerMoved;
            inkCanvas.InkPresenter.UnprocessedInput.PointerReleased +=
                UnprocessedInput_PointerReleased;
        }

        // Handle new ink or erase strokes to clean up selection UI.
        private void StrokeInput_StrokeStarted(
            InkStrokeInput sender, Windows.UI.Core.PointerEventArgs args)
        {
            ClearSelection();
        }

        private void InkPresenter_StrokesErased(
            InkPresenter sender, InkStrokesErasedEventArgs args)
        {
            ClearSelection();
        }

        private void cutButton_Click(object sender, RoutedEventArgs e)
        {
            inkCanvas.InkPresenter.StrokeContainer.CopySelectedToClipboard();
            inkCanvas.InkPresenter.StrokeContainer.DeleteSelected();
            ClearSelection();
        }

        private void copyButton_Click(object sender, RoutedEventArgs e)
        {
            inkCanvas.InkPresenter.StrokeContainer.CopySelectedToClipboard();
        }

        private void pasteButton_Click(object sender, RoutedEventArgs e)
        {
            if (inkCanvas.InkPresenter.StrokeContainer.CanPasteFromClipboard())
            {
                inkCanvas.InkPresenter.StrokeContainer.PasteFromClipboard(
                    new Point(0, 0));
            }
            else
            {
                // Cannot paste from clipboard.
            }
        }

        // Clean up selection UI.
        private void ClearSelection()
        {
            var strokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
            foreach (var stroke in strokes)
            {
                stroke.Selected = false;
            }
            ClearBoundingRect();
        }

        private void ClearBoundingRect()
        {
            if (selectionCanvas.Children.Any())
            {
                selectionCanvas.Children.Clear();
                boundingRect = Rect.Empty;
            }
        }

        // Handle unprocessed pointer events from modified input.
        // The input is used to provide selection functionality.
        // Selection UI is drawn on a canvas under the InkCanvas.
        private void UnprocessedInput_PointerPressed(
            InkUnprocessedInput sender, PointerEventArgs args)
        {
            // Initialize a selection lasso.
            lasso = new Polyline()
            {
                Stroke = new SolidColorBrush(Windows.UI.Colors.Blue),
                StrokeThickness = 1,
                StrokeDashArray = new DoubleCollection() { 5, 2 },
            };

            lasso.Points.Add(args.CurrentPoint.RawPosition);

            selectionCanvas.Children.Add(lasso);
        }

        private void UnprocessedInput_PointerMoved(
            InkUnprocessedInput sender, PointerEventArgs args)
        {
            // Add a point to the lasso Polyline object.
            lasso.Points.Add(args.CurrentPoint.RawPosition);
        }

        private void UnprocessedInput_PointerReleased(
            InkUnprocessedInput sender, PointerEventArgs args)
        {
            // Add the final point to the Polyline object and 
            // select strokes within the lasso area.
            // Draw a bounding box on the selection canvas 
            // around the selected ink strokes.
            lasso.Points.Add(args.CurrentPoint.RawPosition);

            boundingRect =
                inkCanvas.InkPresenter.StrokeContainer.SelectWithPolyLine(
                    lasso.Points);

            DrawBoundingRect();
        }

        // Draw a bounding rectangle, on the selection canvas, encompassing 
        // all ink strokes within the lasso area.
        private void DrawBoundingRect()
        {
            // Clear all existing content from the selection canvas.
            selectionCanvas.Children.Clear();

            // Draw a bounding rectangle only if there are ink strokes 
            // within the lasso area.
            if (!((boundingRect.Width == 0) ||
                (boundingRect.Height == 0) ||
                boundingRect.IsEmpty))
            {
                var rectangle = new Rectangle()
                {
                    Stroke = new SolidColorBrush(Windows.UI.Colors.Blue),
                    StrokeThickness = 1,
                    StrokeDashArray = new DoubleCollection() { 5, 2 },
                    Width = boundingRect.Width,
                    Height = boundingRect.Height
                };

                Canvas.SetLeft(rectangle, boundingRect.X);
                Canvas.SetTop(rectangle, boundingRect.Y);

                selectionCanvas.Children.Add(rectangle);
            }
        }
    }
}

Encre de rendu personnalisé

Par défaut, l’entrée manuscrite est traitée sur un thread d’arrière-plan à faible latence et rendue « humide » telle qu’elle est dessinée. Une fois le trait terminé (stylet ou bouton levé par le doigt, ou bouton de la souris libéré), le trait est traité sur le thread d’interface utilisateur et rendu « sec » sur la couche InkCanvas (au-dessus du contenu de l’application et en remplaçant l’encre humide).

La plateforme d’entrée manuscrite vous permet de remplacer ce comportement et de personnaliser complètement l’expérience d’entrée manuscrite en séchant personnalisée l’entrée manuscrite.

Pour plus d’informations sur le séchage personnalisé, consultez interactions avec stylet et Windows Ink dans les applications Windows.

Remarque

Séchage personnalisé et InkToolbar
Si votre application remplace le comportement de rendu d’encre par défaut de l’InkPresenter avec une implémentation de séchage personnalisée, les traits d’encre rendus ne sont plus disponibles pour inkToolbar et les commandes d’effacement intégrées de InkToolbar ne fonctionnent pas comme prévu. Pour fournir des fonctionnalités d’effacement, vous devez gérer tous les événements de pointeur, effectuer des tests de positionnement sur chaque trait et remplacer la commande intégrée « Effacer toutes les entrées manuscrites ».

Exemples de la rubrique

Autres exemples