Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Important
InkCanvas, InkToolbaret InkPresenter sont des API expérimentales. La prise en charge de l’entrée manuscrite a été ajoutée dans SDK d'application Windows version expérimentale 2.4.1. Cet article décrit SDK d'application Windows 2.5.4-experimental, qui a modifié la façon de connecter un InkToolbar. Ces API ne sont pas disponibles dans le canal stable. Ils peuvent changer ou être supprimés dans une version ultérieure. Ne les utilisez pas dans les applications de production.
Utilisez le contrôle expérimental InkCanvas pour permettre aux utilisateurs de dessiner des entrées manuscrites avec un stylet, une souris ou avec le doigt. Ajoutez un InkToolbar pour donner aux utilisateurs un stylo à bille, un crayon, un surligneur et une gomme, avec des options de couleur et de taille. Chaque canevas possède un InkPresenter qui collecte et restitue l’entrée manuscrite. Le InkStrokeContainer du présentateur stocke les traits afin que vous puissiez enregistrer, charger, copier et créer de l’entrée manuscrite dans le code.
Pour essayer les contrôles, installez le package SDK d'application Windows 2.5.4-experimental et le runtime correspondant. Pour obtenir des liens de téléchargement et des informations d’installation, consultez Les derniers téléchargements SDK d'application Windows.
Est-ce le contrôle approprié ?
Utilisez un canevas d’entrée manuscrite lorsque les utilisateurs doivent :
- Prenez des notes manuscrites ou annotez du contenu.
- Esquissez, dessinez des diagrammes ou signez votre nom.
- Créez du contenu à main levée que votre application enregistre et leur permet de le modifier plus tard.
Les API d’encrage expérimentales n’incluent pas la reconnaissance de l’écriture manuscrite ou des formes. Pour collecter du texte, utilisez une zone de texte.
Si vous avez besoin d’entrée manuscrite dans une application de production, consultez les limitations connues des composants pour connaître l’état de ces composants dans le canal stable et les solutions de remplacement.
Comprendre le modèle d’encrage
Les API d’entrée manuscrite fonctionnent ensemble :
| API | Rôle |
|---|---|
InkCanvas |
Élément XAML qui affiche l’encre et reçoit une entrée à l’encre. La propriété InkPresenter retourne le présentateur du canevas. |
InkPresenter |
Collecte les entrées manuscrites, affiche les traits et contient les paramètres d’entrée et les attributs de dessin par défaut. Vous ne pouvez pas en créer un InkPresenter vous-même. Obtenez-le depuis InkCanvas.InkPresenter. |
InkStrokeContainer |
Stocke les tracés. Utilisez-le pour enregistrer, charger, sélectionner, copier et coller de l’encre. Obtenez-le de InkPresenter.StrokeContainer. |
InkToolbar |
Barre d’outils facultative qui permet aux utilisateurs de choisir un stylet, un crayon, un surligneur ou une gomme, et de modifier la couleur et la taille de l’encre. |
InkCanvas n’a pas de propriété Background. Pour donner à la zone de dessin un arrière-plan et une bordure, placez le canevas à l’intérieur d’un conteneur, par exemple, Borderet définissez la taille sur le conteneur. La couleur d’encre par défaut est noire. Par conséquent, les exemples de cet article utilisent un arrière-plan blanc.
Plusieurs de ces types ont les mêmes noms que les types dans l’espace de noms Windows Runtime Windows.UI.Input.Inking, tels que InkPresenter, InkStrokeContainer et InkHighContrastAdjustment. Les contrôles d’encrage utilisent les versions Microsoft.UI.Xaml.Controls. Les traits et les attributs de dessin utilisent toujours des types Windows Runtime, tels que InkStroke, InkDrawingAttributeset InkStrokeBuilder. Si vous importez les deux espaces de noms et utilisez un nom ambigu, le compilateur C# signale l’erreur CS0104.
Les exemples de cet article importent Microsoft.UI.Xaml.Controls et utilisent des directives d’alias pour les Windows.UI.Input.Inking types dont ils ont besoin :
using Microsoft.UI.Xaml.Controls;
using InkDrawingAttributes = Windows.UI.Input.Inking.InkDrawingAttributes;
using InkStroke = Windows.UI.Input.Inking.InkStroke;
Vous pouvez également importer à la fois des espaces de noms et utiliser des noms pleinement qualifiés pour les types ambigus, comme le fait l’exemple de galerie WinUI 3.
InkPresenter fournit également des API de niveau inférieur que cet article ne couvre pas :
-
StrokeInputdéclenche des événements lorsqu’un trait démarre, continue, se termine ou est annulé. -
UnprocessedInputdéclenche des événements de pointeur pour l’entrée que le présentateur ne traite pas comme entrée manuscrite. -
InputConfigurationdéfinit si le présentateur traite l’entrée à partir de la gomme d’un stylet et du bouton latéral principal. -
InputProcessingConfigurationdéfinit si la saisie dessine des entrées manuscrites, efface les entrées manuscrites ou n’est pas traitée, et comment le présentateur gère le glisser avec le bouton droit. -
ActivateCustomDryingretourne un(e)InkSynchronizerpour les applications qui affichent elles-mêmes les entrées manuscrites validées.
Configuration de votre projet
Les API d’entrée manuscrite sont marquées expérimentales. Lorsque vous les utilisez dans un projet C#, le compilateur C# signale l’avertissement CS8305 et le compilateur XAML signale l’avertissement WMC1501. Si votre projet traite les avertissements comme des erreurs, ces avertissements arrêtent la génération.
Après avoir décidé d’utiliser les API expérimentales, vous pouvez supprimer ces avertissements. Ajoutez les propriétés suivantes à votre fichier projet C# (.csproj) :
<PropertyGroup>
<NoWarn>$(NoWarn);CS8305</NoWarn>
<MSBuildWarningsAsMessages>$(MSBuildWarningsAsMessages);WMC1501</MSBuildWarningsAsMessages>
</PropertyGroup>
Les exemples de cet article utilisent C#. Chaque exemple complet montre le code XAML d’une page et la classe code-behind de la page, MainPage.
Créer une zone de dessin manuscrit
![]()
L’application Galerie WinUI 3 inclut des exemples interactifs de contrôles et de fonctionnalités WinUI. Obtenez l’application à partir du Microsoft Store ou parcourez le code source sur GitHub.
L’exemple suivant place un canevas à l’intérieur d’un élément blanc Border. Le code-behind définit les types d’appareils d’entrée afin que les utilisateurs puissent dessiner avec un stylet, une souris ou le tactile.
<Border
Height="300"
Background="White"
BorderBrush="{ThemeResource SystemControlForegroundChromeHighBrush}"
BorderThickness="1">
<InkCanvas x:Name="inkCanvas" AutomationProperties.Name="Drawing surface" />
</Border>
using Microsoft.UI.Xaml.Controls;
using Windows.UI.Core;
public sealed partial class MainPage : Page
{
public MainPage()
{
InitializeComponent();
// Mouse input doesn't draw ink by default. Choose every device type that can draw ink.
inkCanvas.InkPresenter.InputDeviceTypes =
CoreInputDeviceTypes.Pen | CoreInputDeviceTypes.Mouse | CoreInputDeviceTypes.Touch;
}
}
Pour les raccourcir, certains exemples ultérieurs ne répètent pas ce code. Définissez InputDeviceTypes dans chaque page où les utilisateurs dessinent avec une souris.
Connectez une barre d’outils d'encrage
Placez un InkToolbar près du canevas et définissez sa propriété TargetInkCanvas pour connecter les deux :
<StackPanel Spacing="8">
<InkToolbar
x:Name="inkToolbar"
AutomationProperties.Name="Drawing tools"
TargetInkCanvas="{x:Bind inkCanvas}" />
<Border
Height="300"
Background="White"
BorderBrush="{ThemeResource SystemControlForegroundChromeHighBrush}"
BorderThickness="1">
<InkCanvas x:Name="inkCanvas" AutomationProperties.Name="Drawing surface" />
</Border>
</StackPanel>
À partir de SDK d'application Windows 2.5.4-experimental, vous pouvez connecter une barre d’outils directement à une InkPresenter en définissant la TargetInkPresenter propriété. Définissez-le dans le code :
inkToolbar.TargetInkPresenter = inkCanvas.InkPresenter;
Vous pouvez également le définir en XAML : TargetInkPresenter="{x:Bind inkCanvas.InkPresenter}". Définissez uniquement l’une des deux propriétés cibles.
Par défaut, la barre d’outils affiche des boutons de stylo à bille, crayon, surbrillance, gomme et gabarit (règle). Pour modifier les boutons qui s’affichent, consultez Personnaliser la barre d’outils d’entrée manuscrite.
Choisir les périphériques d’entrée
La propriété InputDeviceTypes utilise l’énumération d’indicateurs CoreInputDeviceTypes de l’espace de noms Windows.UI.Core. La saisie à la souris ne dessine pas d’encre par défaut. Combinez les valeurs pour choisir les appareils qui utilisent l’encre :
// Add mouse input and keep the other device types.
inkCanvas.InkPresenter.InputDeviceTypes |= CoreInputDeviceTypes.Mouse;
// Stop accepting ink from touch input.
inkCanvas.InkPresenter.InputDeviceTypes &= ~CoreInputDeviceTypes.Touch;
// Check whether pen input is enabled.
bool isPenEnabled = inkCanvas.InkPresenter.InputDeviceTypes.HasFlag(CoreInputDeviceTypes.Pen);
Pour arrêter la collecte d’encre à partir de chaque appareil, par exemple pour afficher un dessin que les utilisateurs ne peuvent pas modifier, définissez IsInputEnabled sur false:
inkCanvas.InkPresenter.IsInputEnabled = false;
Pour permettre aux utilisateurs d’activer ou de désactiver un appareil d’entrée à partir de la barre d’outils, voir Ajouter un bouton bascule personnalisé.
Définir les attributs de dessin
Attributs de dessin, tels que la couleur, la taille et la forme de pointe du stylet, contrôlent l’apparence des traits. Ils utilisent la classe Windows RuntimeInkDrawingAttributes. Pour modifier les attributs des nouveaux traits, appeler CopyDefaultDrawingAttributes, modifier la copie et la passer à UpdateDefaultDrawingAttributes:
InkDrawingAttributes attributes = inkCanvas.InkPresenter.CopyDefaultDrawingAttributes();
attributes.Color = Colors.DarkBlue;
attributes.Size = new Size(4, 4);
inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(attributes);
Colors se trouve dans l’espace Microsoft.UI de noms et Size se trouve dans l’espace Windows.Foundation de noms. Les nouveaux attributs s’appliquent aux tracés que les utilisateurs dessinent après l’appel de fonction. Les traits existants conservent leurs attributs.
Lorsque vous connectez un InkToolbar, l’outil actif et ses paramètres de couleur et de taille déterminent les attributs de dessin. Pour lire les attributs actuels, utilisez la propriété InkDrawingAttributes de la barre d’outils.
Créer des paramètres prédéfinis de stylet
Si vous n’utilisez pas de barre d’outils, vous pouvez proposer vos propres préréglages du stylet. L’exemple suivant applique les attributs de stylo à bille, de crayon, de surligneur ou de calligraphie lorsque l’utilisateur sélectionne un préréglage ou modifie la taille.
<StackPanel Spacing="12">
<RadioButtons
x:Name="presetButtons"
Header="Pen preset"
MaxColumns="4"
SelectedIndex="0"
SelectionChanged="PresetButtons_SelectionChanged">
<x:String>Ballpoint</x:String>
<x:String>Pencil</x:String>
<x:String>Highlighter</x:String>
<x:String>Calligraphy</x:String>
</RadioButtons>
<Slider
x:Name="sizeSlider"
Width="240"
HorizontalAlignment="Left"
Header="Size"
Maximum="16"
Minimum="1"
Value="4"
ValueChanged="SizeSlider_ValueChanged" />
<Border
Width="480"
Height="280"
HorizontalAlignment="Left"
Background="White"
BorderBrush="{ThemeResource SystemControlForegroundChromeHighBrush}"
BorderThickness="1">
<InkCanvas x:Name="inkCanvas" AutomationProperties.Name="Drawing surface" />
</Border>
</StackPanel>
using System;
using System.Numerics;
using Microsoft.UI;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Windows.Foundation;
using Windows.UI.Core;
using InkDrawingAttributes = Windows.UI.Input.Inking.InkDrawingAttributes;
using PenTipShape = Windows.UI.Input.Inking.PenTipShape;
public sealed partial class MainPage : Page
{
public MainPage()
{
InitializeComponent();
inkCanvas.InkPresenter.InputDeviceTypes =
CoreInputDeviceTypes.Pen | CoreInputDeviceTypes.Mouse | CoreInputDeviceTypes.Touch;
ApplyPreset();
}
private void PresetButtons_SelectionChanged(object sender, SelectionChangedEventArgs e) => ApplyPreset();
private void SizeSlider_ValueChanged(object sender, RangeBaseValueChangedEventArgs e) => ApplyPreset();
private void ApplyPreset()
{
// SelectionChanged and ValueChanged can occur while the page is still loading.
if (inkCanvas is null || presetButtons is null || sizeSlider is null)
{
return;
}
double size = sizeSlider.Value;
InkDrawingAttributes attributes;
switch (presetButtons.SelectedItem as string)
{
case "Pencil":
// CreateForPencil returns attributes for pencil-style strokes.
attributes = InkDrawingAttributes.CreateForPencil();
attributes.Color = Colors.DimGray;
attributes.Size = new Size(size, size);
break;
case "Highlighter":
attributes = new InkDrawingAttributes
{
Color = Colors.Yellow,
DrawAsHighlighter = true,
PenTip = PenTipShape.Rectangle,
Size = new Size(size, size * 3),
};
break;
case "Calligraphy":
attributes = new InkDrawingAttributes
{
Color = Colors.DarkBlue,
PenTip = PenTipShape.Rectangle,
Size = new Size(Math.Max(1, size / 2), size * 2),
// Rotate the nib 45 degrees so the stroke width changes with the stroke direction.
PenTipTransform = Matrix3x2.CreateRotation(MathF.PI / 4),
};
break;
default:
attributes = new InkDrawingAttributes
{
Color = Colors.Black,
PenTip = PenTipShape.Circle,
Size = new Size(size, size),
};
break;
}
// The new attributes apply to strokes that the user draws after this call.
inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(attributes);
}
}
-
InkDrawingAttributes.CreateForPencilretourne des attributs pour les traits de crayon. La propriétéKindest en lecture seule. Utilisez donc cette méthode pour créer des attributs de stylet. -
DrawAsHighlighteraffiche le trait sous forme de superposition de surligneur. -
PenTipdéfinit la forme de l’extrémité du stylet etPenTipTransformla fait pivoter ou la met à l’échelle. Une pointe rectangulaire inclinée produit des traits de calligraphie.
Répondre aux modifications d’encre
Le présentateur déclenche StrokesCollected après qu’un utilisateur dessine des traits et StrokesErased après qu’un utilisateur les efface. La Strokes propriété des arguments de chaque événement contient les traits affectés. Le code suivant conserve un compteur de frappes à jour dans un TextBlock nommé strokeCountText:
inkCanvas.InkPresenter.StrokesCollected += (sender, args) => UpdateStrokeCount();
inkCanvas.InkPresenter.StrokesErased += (sender, args) => UpdateStrokeCount();
private void UpdateStrokeCount()
{
int count = inkCanvas.InkPresenter.StrokeContainer.GetStrokes().Count;
strokeCountText.Text = $"{count} strokes";
}
Ces événements se produisent lorsque les utilisateurs dessinent ou effacent de l’encre. Lorsque vous modifiez des traits dans le code, par exemple avec Clear, LoadAsyncou AddStroke, mettez à jour votre interface utilisateur après l’appel.
Enregistrer et charger de l’encre
InkStrokeContainer enregistre l’encre au format sérialisé manuscrit (ISF). Pour choisir la façon dont les données sont stockées, transmettez une InkPersistenceFormat valeur à SaveAsync:
| Format | Output |
|---|---|
GifWithEmbeddedIsf |
Image GIF qui contient les données manuscrites. Les visionneuses d’images peuvent afficher le fichier, et votre application peut charger à nouveau l’encre pour modification. |
Isf |
Données manuscrites uniquement, sans image. Il s’agit du format le plus compact. |
Ajoutez les méthodes suivantes au code-behind d’une page qui contient un InkCanvas nommé inkCanvas.
SaveInkAsync écrit l’entrée manuscrite dans un fichier et LoadInkAsync remplace l’entrée manuscrite sur la zone de dessin par l’entrée manuscrite d’un fichier.
LoadAsync supprime les traits existants avant d’ajouter les traits chargés. Vous n’avez donc pas besoin d’appeler Clear en premier.
using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.UI.Xaml.Controls;
using Windows.Storage.Streams;
using InkPersistenceFormat = Windows.UI.Input.Inking.InkPersistenceFormat;
public sealed partial class MainPage : Page
{
private async Task SaveInkAsync(string path)
{
InkStrokeContainer container = inkCanvas.InkPresenter.StrokeContainer;
// File.Create replaces any existing file, so the file contains only the new ink.
using FileStream fileStream = File.Create(path);
using IOutputStream output = fileStream.AsOutputStream();
// GifWithEmbeddedIsf writes a GIF image that also contains the editable ink data.
await container.SaveAsync(output, InkPersistenceFormat.GifWithEmbeddedIsf);
await output.FlushAsync();
}
private async Task LoadInkAsync(string path)
{
InkStrokeContainer container = inkCanvas.InkPresenter.StrokeContainer;
using FileStream fileStream = File.OpenRead(path);
using IInputStream input = fileStream.AsInputStream();
// LoadAsync removes the current strokes before it adds the loaded strokes.
await container.LoadAsync(input);
}
}
AsOutputStream et AsInputStream convertissent les flux .NET en types de flux du runtime Windows utilisés par SaveAsync et LoadAsync. Pour attendre ces méthodes Windows Runtime, le fichier a besoin de la directive using System;. Sans cela, le compilateur signale l’erreur CS4036.
Pour permettre aux utilisateurs de choisir un fichier, utilisez les sélecteurs de fichiers SDK d'application Windows, qui retournent le chemin d’accès du fichier sélectionné. Pour plus d’informations, consultez Enregistrer un fichier avec un sélecteur de SDK d'application Windows et Ouvrir des fichiers et dossiers avec les sélecteurs SDK d'application Windows.
Pour conserver l’entrée manuscrite en mémoire, par exemple pour la restaurer ultérieurement dans la même session, enregistrez-la dans un InMemoryRandomAccessStream. Passez le résultat du GetOutputStreamAt(0) flux à SaveAsync, puis passez son GetInputStreamAt(0) résultat à LoadAsync. L’exemple de galerie WinUI 3 utilise cette approche.
Copier et coller de l’encre
InkStrokeContainer peut copier des tracés dans le presse-papiers et coller de l’encre à partir de celui-ci.
CopySelectedToClipboard ne copie que les traits sélectionnés, de sorte que l’exemple suivant sélectionne chaque trait sur le canevas gauche avant de les copier.
CanPasteFromClipboard vérifie si le Presse-papiers contient des entrées manuscrites.
PasteFromClipboard ajoute l’encre à la position que vous passez et retourne le rectangle englobant de la zone qui a changé.
<StackPanel Spacing="12">
<StackPanel Orientation="Horizontal" Spacing="8">
<Button Click="CopyButton_Click" Content="Copy left canvas" />
<Button
x:Name="pasteButton"
Click="PasteButton_Click"
Content="Paste into right canvas"
IsEnabled="False" />
</StackPanel>
<StackPanel Orientation="Horizontal" Spacing="12">
<Border
Width="300"
Height="220"
Background="White"
BorderBrush="{ThemeResource SystemControlForegroundChromeHighBrush}"
BorderThickness="1">
<InkCanvas x:Name="sourceCanvas" AutomationProperties.Name="Source drawing surface" />
</Border>
<Border
Width="300"
Height="220"
Background="White"
BorderBrush="{ThemeResource SystemControlForegroundChromeHighBrush}"
BorderThickness="1">
<InkCanvas x:Name="targetCanvas" AutomationProperties.Name="Target drawing surface" />
</Border>
</StackPanel>
<TextBlock x:Name="statusText" Text="Draw on the left canvas, then copy it." TextWrapping="Wrap" />
</StackPanel>
using System.Collections.Generic;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Windows.Foundation;
using Windows.UI.Core;
using InkStroke = Windows.UI.Input.Inking.InkStroke;
public sealed partial class MainPage : Page
{
public MainPage()
{
InitializeComponent();
foreach (InkCanvas canvas in new[] { sourceCanvas, targetCanvas })
{
canvas.InkPresenter.InputDeviceTypes =
CoreInputDeviceTypes.Pen | CoreInputDeviceTypes.Mouse | CoreInputDeviceTypes.Touch;
}
}
private void CopyButton_Click(object sender, RoutedEventArgs e)
{
InkStrokeContainer source = sourceCanvas.InkPresenter.StrokeContainer;
IReadOnlyList<InkStroke> strokes = source.GetStrokes();
if (strokes.Count == 0)
{
statusText.Text = "Draw on the left canvas first.";
return;
}
// CopySelectedToClipboard copies only selected strokes, so select all strokes first.
foreach (InkStroke stroke in strokes)
{
stroke.Selected = true;
}
source.CopySelectedToClipboard();
foreach (InkStroke stroke in strokes)
{
stroke.Selected = false;
}
pasteButton.IsEnabled = targetCanvas.InkPresenter.StrokeContainer.CanPasteFromClipboard();
statusText.Text = $"Copied {strokes.Count} stroke(s) to the clipboard.";
}
private void PasteButton_Click(object sender, RoutedEventArgs e)
{
InkStrokeContainer target = targetCanvas.InkPresenter.StrokeContainer;
if (!target.CanPasteFromClipboard())
{
statusText.Text = "The clipboard doesn't contain ink.";
return;
}
// Place the pasted ink near the upper-left corner of the target canvas.
Rect pasted = target.PasteFromClipboard(new Point(16, 16));
statusText.Text = $"Pasted ink into a {pasted.Width:0} x {pasted.Height:0} area.";
}
}
Pour manipuler certains traits, définissez la propriété Selected de chaque trait souhaité, ou appelez SelectWithLine ou SelectWithPolyLine. Pour supprimer ou déplacer les traits sélectionnés, appelez DeleteSelected ou MoveSelected.
Créer des tracés dans le code
Utilisez la classe Windows Runtime InkStrokeBuilder pour créer des traits à partir de points et les ajouter au canevas avec AddStroke. L’exemple suivant ajoute une étoile et une ligne de signature.
<StackPanel Spacing="12">
<StackPanel Orientation="Horizontal" Spacing="8">
<Button Click="AddStarButton_Click" Content="Add star" />
<Button Click="AddSignatureLineButton_Click" Content="Add signature line" />
<Button Click="ClearButton_Click" Content="Clear" />
</StackPanel>
<Border
Width="480"
Height="280"
HorizontalAlignment="Left"
Background="White"
BorderBrush="{ThemeResource SystemControlForegroundChromeHighBrush}"
BorderThickness="1">
<InkCanvas x:Name="inkCanvas" AutomationProperties.Name="Drawing surface" />
</Border>
</StackPanel>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Microsoft.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Windows.Foundation;
using Windows.UI;
using InkDrawingAttributes = Windows.UI.Input.Inking.InkDrawingAttributes;
using InkPoint = Windows.UI.Input.Inking.InkPoint;
using InkStroke = Windows.UI.Input.Inking.InkStroke;
using InkStrokeBuilder = Windows.UI.Input.Inking.InkStrokeBuilder;
public sealed partial class MainPage : Page
{
public MainPage()
{
InitializeComponent();
}
private void AddStarButton_Click(object sender, RoutedEventArgs e)
{
// Alternate between outer and inner points to draw a five-pointed star.
Point center = new(inkCanvas.ActualWidth / 2, 90);
List<Point> points = new();
for (int i = 0; i <= 10; i++)
{
double radius = i % 2 == 0 ? 70 : 28;
double angle = -Math.PI / 2 + i * Math.PI / 5;
points.Add(new Point(center.X + radius * Math.Cos(angle), center.Y + radius * Math.Sin(angle)));
}
AddStroke(points, Colors.Goldenrod, 4);
}
private void AddSignatureLineButton_Click(object sender, RoutedEventArgs e)
{
double left = 40;
double right = inkCanvas.ActualWidth - 40;
double baseline = inkCanvas.ActualHeight - 40;
AddStroke(new[] { new Point(left, baseline), new Point(right, baseline) }, Colors.Gray, 2);
AddStroke(new[] { new Point(left, baseline - 28), new Point(left + 18, baseline - 10) }, Colors.Gray, 2);
AddStroke(new[] { new Point(left + 18, baseline - 28), new Point(left, baseline - 10) }, Colors.Gray, 2);
}
private void ClearButton_Click(object sender, RoutedEventArgs e)
{
inkCanvas.InkPresenter.StrokeContainer.Clear();
}
private void AddStroke(IEnumerable<Point> points, Color color, double size)
{
InkStrokeBuilder builder = new();
builder.SetDefaultDrawingAttributes(new InkDrawingAttributes
{
Color = color,
Size = new Size(size, size),
// Keep straight segments and sharp corners instead of smoothing them into curves.
FitToCurve = false,
});
// Give every point the same pressure so that the stroke has a uniform width.
IEnumerable<InkPoint> inkPoints = points.Select(point => new InkPoint(point, 0.5f));
InkStroke stroke = builder.CreateStrokeFromInkPoints(inkPoints, Matrix3x2.Identity);
inkCanvas.InkPresenter.StrokeContainer.AddStroke(stroke);
}
}
-
CreateStrokeFromInkPointscrée un trait à partir deInkPointvaleurs. Chaque point combine une position sur le canvas avec une valeur de pression. - La documentation Windows Runtime recommande
CreateStrokeFromInkPointsplutôt queCreateStrokepour les traits que vous ajoutez à un présentateur d’encre. - Quand
FitToCurvec’estfalsele cas, le trait utilise des segments droits entre des points plutôt que des courbes.
Un trait ne peut appartenir qu’à un seul conteneur. Pour copier des traits dans une autre zone de dessin, ajoutez des clones des traits :
private void CopyStrokesTo(InkCanvas target)
{
foreach (InkStroke stroke in inkCanvas.InkPresenter.StrokeContainer.GetStrokes())
{
target.InkPresenter.StrokeContainer.AddStroke(stroke.Clone());
}
}
Personnaliser la barre d’outils d’entrée manuscrite
Choisir les contrôles initiaux
La propriété InitialControls détermine quels boutons intégrés la barre d’outils crée :
| Value | Boutons intégrés |
|---|---|
All (valeur par défaut) |
stylo à bille, crayon, surligneur, gomme et gabarit (règle) |
PensOnly |
Stylo à bille, crayon et surligneur |
AllExceptPens |
Gomme et gabarit (règle) |
None |
None. Déclarez les boutons souhaités en tant qu’enfants de la barre d’outils. |
L’exemple suivant déclare les quatre mêmes boutons que l’exemple de galerie WinUI 3, donne à chacun un nom accessible et sélectionne le stylo à bille lorsque la barre d’outils se charge :
<StackPanel Spacing="8">
<InkToolbar
x:Name="inkToolbar"
AutomationProperties.Name="Drawing tools"
InitialControls="None"
TargetInkCanvas="{x:Bind inkCanvas}">
<InkToolbarBallpointPenButton x:Name="ballpointButton" AutomationProperties.Name="Ballpoint pen" />
<InkToolbarPencilButton AutomationProperties.Name="Pencil" />
<InkToolbarHighlighterButton AutomationProperties.Name="Highlighter" />
<InkToolbarEraserButton AutomationProperties.Name="Eraser" />
</InkToolbar>
<Border
Height="300"
Background="White"
BorderBrush="{ThemeResource SystemControlForegroundChromeHighBrush}"
BorderThickness="1">
<InkCanvas x:Name="inkCanvas" AutomationProperties.Name="Drawing surface" />
</Border>
</StackPanel>
using Microsoft.UI.Xaml.Controls;
public sealed partial class MainPage : Page
{
public MainPage()
{
InitializeComponent();
// Select a starting tool after the toolbar loads.
inkToolbar.Loaded += (sender, args) =>
{
if (inkToolbar.ActiveTool is null)
{
inkToolbar.ActiveTool = ballpointButton;
}
};
}
}
Pour afficher une commande qui efface toutes les entrées manuscrites, définissez la propriété du bouton gomme true sur IsClearAllVisible. La barre d’outils déclenche l’événement EraseAllClicked lorsque l’utilisateur sélectionne la commande.
Personnaliser les couleurs et les largeurs d’un stylet
Chaque bouton de stylet a une sélection Palette de pinceaux et de réglages de largeur de trait. Définir Palette dans le code. Pour plus d’informations, consultez Limites connues. L’exemple suivant remplace la palette d’un stylo à bille par quatre couleurs de marque, limite la largeur du trait et affiche les attributs du stylet actif.
<StackPanel Spacing="12">
<InkToolbar
x:Name="inkToolbar"
AutomationProperties.Name="Drawing tools"
InitialControls="AllExceptPens"
TargetInkCanvas="{x:Bind inkCanvas}">
<InkToolbarBallpointPenButton
x:Name="brandPen"
AutomationProperties.Name="Brand pen"
MaxStrokeWidth="16"
MinStrokeWidth="2"
SelectedStrokeWidth="6" />
</InkToolbar>
<RadioButtons
x:Name="colorButtons"
Header="Pen color"
MaxColumns="4"
SelectedIndex="0"
SelectionChanged="ColorButtons_SelectionChanged">
<x:String>Blue</x:String>
<x:String>Green</x:String>
<x:String>Orange</x:String>
<x:String>Purple</x:String>
</RadioButtons>
<TextBlock x:Name="penInfo" />
<Border
Width="480"
Height="240"
HorizontalAlignment="Left"
Background="White"
BorderBrush="{ThemeResource SystemControlForegroundChromeHighBrush}"
BorderThickness="1">
<InkCanvas x:Name="inkCanvas" AutomationProperties.Name="Drawing surface" />
</Border>
</StackPanel>
using System.Linq;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Windows.UI;
public sealed partial class MainPage : Page
{
private static readonly Color[] BrandColors =
{
Color.FromArgb(0xFF, 0x00, 0x78, 0xD4), // Blue
Color.FromArgb(0xFF, 0x10, 0x7C, 0x10), // Green
Color.FromArgb(0xFF, 0xD8, 0x3B, 0x01), // Orange
Color.FromArgb(0xFF, 0x5C, 0x2D, 0x91), // Purple
};
public MainPage()
{
InitializeComponent();
// Replace the pen's default palette and select the first color.
brandPen.Palette = BrandColors.Select(color => (Brush)new SolidColorBrush(color)).ToList();
brandPen.SelectedBrushIndex = 0;
inkToolbar.InkDrawingAttributesChanged += (sender, args) => ShowPenInfo();
// The toolbar sets up its buttons when it loads, so select the starting tool then.
inkToolbar.Loaded += (sender, args) =>
{
inkToolbar.ActiveTool = brandPen;
ShowPenInfo();
};
}
private void ColorButtons_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// SelectionChanged can occur while the page is still loading.
if (brandPen is null || colorButtons is null || penInfo is null || colorButtons.SelectedIndex < 0)
{
return;
}
// SelectedBrushIndex selects an entry in Palette, like choosing a color in the pen flyout.
brandPen.SelectedBrushIndex = colorButtons.SelectedIndex;
inkToolbar.ActiveTool = brandPen;
ShowPenInfo();
}
private void ShowPenInfo()
{
// InkDrawingAttributes describes the ink that the active tool draws.
if (inkToolbar.InkDrawingAttributes is { } attributes)
{
penInfo.Text = $"Color {attributes.Color}, width {attributes.Size.Width:0} (range {brandPen.MinStrokeWidth:0}-{brandPen.MaxStrokeWidth:0})";
}
}
}
SelectedBrushIndex sélectionne une couleur dans Palette.
SelectedStrokeWidth, MinStrokeWidth et MaxStrokeWidth définissent la largeur actuelle du trait et la plage parmi laquelle les utilisateurs peuvent choisir. La barre d’outils émet InkDrawingAttributesChanged lorsque les attributs de l’outil actif changent.
Sélectionner l’outil actif
Définissez ActiveTool pour sélectionner un outil et gérez ActiveToolChanged pour réagir lorsque l’utilisateur en sélectionne un autre.
GetToolButton retourne un bouton d’outil intégré du type que vous passez en paramètre. Les boutons intégrés peuvent ne pas exister tant que la barre d’outils n’est pas chargée ; sélectionnez donc un outil initial dans le gestionnaire d’événements Loaded de la barre d’outils.
<StackPanel Spacing="12">
<InkToolbar
x:Name="inkToolbar"
AutomationProperties.Name="Drawing tools"
TargetInkCanvas="{x:Bind inkCanvas}" />
<TextBlock x:Name="toolInfo" />
<Border
Width="480"
Height="280"
HorizontalAlignment="Left"
Background="White"
BorderBrush="{ThemeResource SystemControlForegroundChromeHighBrush}"
BorderThickness="1">
<InkCanvas x:Name="inkCanvas" AutomationProperties.Name="Drawing surface" />
</Border>
</StackPanel>
using Microsoft.UI.Xaml.Controls;
public sealed partial class MainPage : Page
{
public MainPage()
{
InitializeComponent();
inkToolbar.ActiveToolChanged += (sender, args) => ShowToolInfo();
inkToolbar.InkDrawingAttributesChanged += (sender, args) => ShowToolInfo();
// The toolbar creates its built-in buttons when it loads, so select the starting tool then.
inkToolbar.Loaded += (sender, args) =>
{
inkToolbar.ActiveTool = inkToolbar.GetToolButton(InkToolbarTool.Pencil);
ShowToolInfo();
};
}
private void ShowToolInfo()
{
if (inkToolbar.ActiveTool is not { } tool)
{
toolInfo.Text = "No active tool";
return;
}
toolInfo.Text = inkToolbar.InkDrawingAttributes is { } attributes
? $"{tool.ToolKind}: {attributes.Kind} ink, color {attributes.Color}, size {attributes.Size.Width:0}"
: $"{tool.ToolKind}";
}
}
Changer l’orientation
Pour afficher la barre d’outils verticalement, par exemple en regard du canevas, définissez Orientation sur Vertical:
<Grid HorizontalAlignment="Left" ColumnSpacing="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<InkToolbar
VerticalAlignment="Top"
AutomationProperties.Name="Drawing tools"
Orientation="Vertical"
TargetInkCanvas="{x:Bind inkCanvas}" />
<Border
Grid.Column="1"
Width="440"
Height="320"
VerticalAlignment="Top"
Background="White"
BorderBrush="{ThemeResource SystemControlForegroundChromeHighBrush}"
BorderThickness="1">
<InkCanvas x:Name="inkCanvas" AutomationProperties.Name="Drawing surface" />
</Border>
</Grid>
La ButtonFlyoutPlacement propriété définit l’emplacement où les menus volants de bouton s’ouvrent par rapport à la barre d’outils. La valeur par défaut, Auto, permet à la barre d’outils de choisir la position.
Ajouter un bouton bascule personnalisé
Utilisez un InkToolbarCustomToggleButton pour ajouter un paramètre que les utilisateurs activent et désactivent à partir de la barre d’outils. L’exemple suivant ajoute un bouton bascule qui contrôle l’entrée tactile. La saisie au stylet et à la souris tracent toujours de l’encre.
<StackPanel Spacing="12">
<InkToolbar AutomationProperties.Name="Drawing tools" TargetInkCanvas="{x:Bind inkCanvas}">
<InkToolbarCustomToggleButton
x:Name="touchWritingToggle"
AutomationProperties.Name="Touch writing"
Checked="TouchWritingToggle_Changed"
ToolTipService.ToolTip="Touch writing"
Unchecked="TouchWritingToggle_Changed">
<SymbolIcon Symbol="TouchPointer" />
</InkToolbarCustomToggleButton>
</InkToolbar>
<TextBlock x:Name="deviceInfo" />
<Border
Width="480"
Height="280"
HorizontalAlignment="Left"
Background="White"
BorderBrush="{ThemeResource SystemControlForegroundChromeHighBrush}"
BorderThickness="1">
<InkCanvas x:Name="inkCanvas" AutomationProperties.Name="Drawing surface" />
</Border>
</StackPanel>
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Windows.UI.Core;
public sealed partial class MainPage : Page
{
public MainPage()
{
InitializeComponent();
// Pen and mouse input always draw ink. The custom toggle controls touch input.
inkCanvas.InkPresenter.InputDeviceTypes = CoreInputDeviceTypes.Pen | CoreInputDeviceTypes.Mouse;
ShowDevices();
}
private void TouchWritingToggle_Changed(object sender, RoutedEventArgs e)
{
if (touchWritingToggle.IsChecked == true)
{
inkCanvas.InkPresenter.InputDeviceTypes |= CoreInputDeviceTypes.Touch;
}
else
{
inkCanvas.InkPresenter.InputDeviceTypes &= ~CoreInputDeviceTypes.Touch;
}
ShowDevices();
}
private void ShowDevices()
{
deviceInfo.Text = $"InputDeviceTypes = {inkCanvas.InkPresenter.InputDeviceTypes}";
}
}
Le bouton bascule affiche uniquement une icône, de sorte que l’exemple lui donne un nom accessible et une info-bulle. Dans cet exemple, la barre d’outils crée également ses boutons par défaut et le bouton bascule personnalisé s’affiche après eux.
La barre d’outils prend en charge d’autres boutons personnalisés :
-
InkToolbarCustomToolButtonajoute un outil personnalisé. -
InkToolbarCustomPenButtonajoute une plume qui crée ses propres attributs de dessin. Définissez sa propriétéCustomPensur une instance d’une classe qui dérive deInkToolbarCustomPenet remplace la méthodeCreateInkDrawingAttributesCore.
Créer la barre d’outils dans le code
Vous pouvez également créer le canevas et la barre d’outils dans le code. L’exemple suivant les ajoute à un Border nommé inkHost, connecte la barre d’outils avec TargetInkPresenter, puis sélectionne le crayon lorsque la barre d’outils se charge.
<Border x:Name="inkHost" HorizontalAlignment="Left" />
using Microsoft.UI;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Automation;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Windows.UI.Core;
public sealed partial class MainPage : Page
{
public MainPage()
{
InitializeComponent();
CreateInkingUI();
}
private void CreateInkingUI()
{
InkCanvas canvas = new();
AutomationProperties.SetName(canvas, "Drawing surface");
canvas.InkPresenter.InputDeviceTypes =
CoreInputDeviceTypes.Pen | CoreInputDeviceTypes.Mouse | CoreInputDeviceTypes.Touch;
InkToolbar toolbar = new() { TargetInkPresenter = canvas.InkPresenter };
AutomationProperties.SetName(toolbar, "Drawing tools");
// The toolbar creates its built-in buttons when it loads.
toolbar.Loaded += (sender, args) => toolbar.ActiveTool = toolbar.GetToolButton(InkToolbarTool.Pencil);
Border canvasFrame = new()
{
Width = 480,
Height = 280,
Background = new SolidColorBrush(Colors.White),
BorderBrush = new SolidColorBrush(Colors.DarkGray),
BorderThickness = new Thickness(1),
Child = canvas,
};
StackPanel layout = new() { Spacing = 8 };
layout.Children.Add(toolbar);
layout.Children.Add(canvasFrame);
inkHost.Child = layout;
}
}
Limitations connues
- Les contrôles d’entrée manuscrite sont disponibles uniquement dans les versions expérimentales du SDK Windows App. Ils ne sont pas disponibles dans le canal stable.
- Les API d’encrage n’incluent pas la reconnaissance de l’écriture manuscrite ou des formes.
- La saisie à la souris n’affiche pas l’encre tant que vous n’avez pas ajouté
CoreInputDeviceTypes.MouseàInputDeviceTypes.
Les problèmes suivants ont été signalés avec SDK d'application Windows 2.4.1-experimental. Les notes de publication 2.5.4-experimental ne les répertorient pas comme corrigés :
- Les pochoirs de règle et de rapporteur ne fonctionnent pas.
-
RenderTargetBitmapne détecte pas l’encre. Pour créer une représentation de l’encre, enregistrez-la au formatGifWithEmbeddedIsf. - La définition de la propriété
Paletted’un bouton du stylet en XAML provoque une erreur d’analyse XAML. DéfinissezPalettedans le code à la place, comme indiqué dans Personnaliser les couleurs et les largeurs d’un stylet. - Les menus déroulants des boutons peuvent apparaître vides, et le fait de modifier
ButtonFlyoutPlacementpeut ne pas changer l’endroit où ils s’ouvrent.
Modifications de 2.4.1-expérimental
Si vous avez utilisé les API d’entrée manuscrite dans SDK d'application Windows version 2.4.1 expérimentale, mettez à jour votre code pour ces modifications dans la version 2.5.4 expérimentale :
| API dans 2.4.1-experimental | Changement dans 2.5.4-experimental |
|---|---|
InkToolbar.TargetInkPresenter (type Object) |
Le type de propriété est InkPresenter. |
Méthodes InkPresenter.GetHighContrastAdjustment et InkPresenter.SetHighContrastAdjustment |
Remplacé par la propriété InkPresenter.HighContrastAdjustment. |
Méthodes InkStrokeInput.GetInkPresenter et InkUnprocessedInput.GetInkPresenter |
Remplacé par une InkPresenter propriété sur chaque classe. |
InkToolbarEraserButton.SelectedEraser, IsStrokeEraserVisibleet ArePrecisionErasersVisible propriétés ; InkToolbar.EraserFlyoutItemClicked événement ; InkToolbarEraserKind, InkToolbarEraserFlyoutItemKindet InkToolbarEraserFlyoutItemClickedEventArgs types |
Supprimé. |
| Non disponible | Méthode InkPresenter.ActivateCustomDrying et classe InkSynchronizer ajoutées. |
Accessibilité et contraste élevé
L’entrée manuscrite est du contenu visuel. Planifiez la façon dont tous les utilisateurs peuvent utiliser vos fonctionnalités d’entrée manuscrite :
- Donnez à chaque canevas et barre d’outils un nom accessible avec
AutomationProperties.Name, comme le montrent les exemples de cet article. Donnez aux boutons personnalisés qui affichent uniquement une icône un nom et une info-bulle. -
InkCanvasAutomationPeerexpose le canevas à UI Automation, mais il ne décrit pas les tracés. Si l’encre a une signification, telle qu’une signature ou un diagramme étiqueté, fournissez une alternative textuelle. - Le dessin à l’encre nécessite une saisie au pointeur. Ne faites pas de l’encre la seule façon d’effectuer une tâche. Par exemple, laissez les utilisateurs taper leur nom comme alternative à la signature.
- Les pairs d’automatisation pour les boutons intégrés de la barre d’outils prennent en charge les modèles de contrôle SelectionItem et ExpandCollapse. Testez la navigation au clavier et les menus déroulants des boutons avec la configuration de la barre d’outils utilisée par votre application.
Lorsqu’un thème de contraste est actif, le présentateur peut remplacer les couleurs de l’encre par les couleurs système. Pour choisir ce comportement, définissez la propriété HighContrastAdjustment sur une valeur InkHighContrastAdjustment de l’espace de noms Microsoft.UI.Xaml.Controls. La documentation Windows Runtime de l’énumération portant le même nom décrit les valeurs de cette façon :
| Value | Couleur de l’encre dans un thème de contraste |
|---|---|
UseSystemColorsWhenNecessary |
La propre couleur de l’encre si elle contraste suffisamment avec l’arrière-plan. Sinon, une couleur système. |
UseSystemColors |
Une couleur système. L’encre du surligneur utilise la couleur du surligneur système. |
UseOriginalColors |
Couleur propre à l’encre. |
inkCanvas.InkPresenter.HighContrastAdjustment = InkHighContrastAdjustment.UseSystemColorsWhenNecessary;
Pour rester concis, les exemples de cet article définissent directement un arrière-plan blanc. Dans votre application, définissez l’arrière-plan du canevas dans les dictionnaires de thèmes afin qu’il suive le thème actif, comme décrit dans Les thèmes Contrast. Testez ensuite votre application avec chaque thème de contraste et vérifiez que l’encre, l’arrière-plan et la bordure restent distincts.
Pour plus d’informations, consultez Exposer les informations d’accessibilité de base et Accessibilité au clavier.
Voir aussi
- Code source de l’exemple InkCanvas de la galerie WinUI 3
- Notes de publication de SDK d'application Windows 2.5.4 - version expérimentale
- Microsoft. Interface utilisateur. Espace de noms Xaml.Controls
- espace de noms Windows.UI.Input.Inking
- Retour haptique du stylet dans les applications Windows
- Interactions de stylet et Windows Ink (UWP)
- Contrôles d’entrée manuscrite (UWP)
- Ce qui est pris en charge lors de la migration d’UWP vers WinUI 3
Windows developer