Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Importante
InkCanvas, InkToolbare InkPresenter sono API sperimentali. Il supporto dell'input penna è stato aggiunto in SDK per app di Windows versione 2.4.1-sperimentale. Questo articolo descrive SDK per app di Windows 2.5.4-experimental, che ha modificato la modalità di connessione di un InkToolbar. Queste API non sono disponibili nel canale stabile. Potrebbero cambiare o essere rimossi in una versione futura. Non usarli nelle app di produzione.
Usare il controllo sperimentale InkCanvas per consentire agli utenti di disegnare tratti a penna con penna, mouse o tocco. Aggiungere un oggetto InkToolbar per offrire agli utenti una penna a sfera, una matita, un evidenziatore e una gomma, con opzioni di colore e dimensioni. Ogni canvas ha un oggetto InkPresenter che raccoglie ed esegue il rendering dell'input penna digitale. Il presenter InkStrokeContainer archivia i tratti, in modo da poter salvare, caricare, copiare e creare input penna nel codice.
Per provare i controlli, installare il pacchetto sperimentale SDK per app di Windows 2.5.4 e il runtime corrispondente. Per i collegamenti di download e le informazioni di configurazione, vedere Download SDK per app di Windows più recenti.
Questo è il controllo giusto?
Usa un'area input penna quando gli utenti devono:
- Prendere note scritte a mano o annotare il contenuto.
- Tracciare, disegnare diagrammi o apporre la propria firma.
- Crea contenuto a mano libera che l'app salva e che possono modificare in un secondo momento.
Le API di inchiostro digitale sperimentali non includono il riconoscimento della grafia o della forma. Per raccogliere testo, usare una casella di testo.
Se è necessario usare l'input penna digitale in un'app di produzione, vedere Gap di controllo noti per lo stato di questi controlli nel canale stabile e per le alternative.
Comprendere il modello di inchiostrazione
Le API di creazione input penna interagiscono tra loro:
| API | Ruolo |
|---|---|
InkCanvas |
Elemento XAML che visualizza i tratti digitali e riceve l'input penna. La proprietà InkPresenter restituisce il presenter per l'area di disegno. |
InkPresenter |
Raccoglie input input penna, esegue il rendering dei tratti e contiene le impostazioni di input e gli attributi predefiniti del disegno. Non è possibile crearne InkPresenter uno. Recuperarlo da InkCanvas.InkPresenter. |
InkStrokeContainer |
Archivia le tracce. Usarlo per salvare, caricare, selezionare, copiare e incollare la manoscrittura. Recuperarlo da InkPresenter.StrokeContainer. |
InkToolbar |
Barra degli strumenti facoltativa che consente agli utenti di scegliere una penna, una matita, un evidenziatore o una gomma e modificare il colore e le dimensioni dell'inchiostro. |
InkCanvas non ha una proprietà Background. Per assegnare all'area di disegno uno sfondo e un bordo, posizionare il canvas all'interno di un contenitore come un Border e impostare le dimensioni nel contenitore. Il colore predefinito dell'inchiostro è nero, quindi gli esempi in questo articolo usano uno sfondo bianco.
Molti di questi tipi hanno gli stessi nomi dei tipi nello spazio dei nomi di Windows Runtime Windows.UI.Input.Inking, ad esempio InkPresenter, InkStrokeContainer e InkHighContrastAdjustment. I controlli della scrittura a penna usano le versioni Microsoft.UI.Xaml.Controls. I tratti e gli attributi di disegno usano ancora tipi Windows Runtime, ad esempio InkStroke, InkDrawingAttributese InkStrokeBuilder. Se si importano entrambi gli spazi dei nomi e si usa un nome ambiguo, il compilatore C# segnala l'errore CS0104.
Gli esempi in questo articolo importano Microsoft.UI.Xaml.Controls e usano direttive alias per i Windows.UI.Input.Inking tipi necessari:
using Microsoft.UI.Xaml.Controls;
using InkDrawingAttributes = Windows.UI.Input.Inking.InkDrawingAttributes;
using InkStroke = Windows.UI.Input.Inking.InkStroke;
È anche possibile importare entrambi gli spazi dei nomi e usare nomi completi per i tipi ambigui, come fa l'esempio WinUI 3 Gallery.
InkPresenter fornisce anche API di livello inferiore che questo articolo non illustra:
-
StrokeInputgenera eventi quando un tratto inizia, continua, termina o viene annullato. -
UnprocessedInputgenera eventi puntatore per l'input che il presenter non elabora come input penna. -
InputConfigurationimposta se il presenter elabora l'input dalla gomma di una penna e dal pulsante laterale principale. -
InputProcessingConfigurationimposta se l'input disegna tratti penna, cancella l'input penna o non viene elaborato e il modo in cui il presentatore gestisce l'input di trascinamento destro. -
ActivateCustomDryingrestituisce un oggettoInkSynchronizerper le app che eseguono il rendering dell'input penna confermato.
Configurare il progetto
Le API di inchiostro digitale sono contrassegnate come sperimentali. Quando vengono usati in un progetto C#, il compilatore C# segnala l'avviso CS8305 e il compilatore XAML segnala l'avviso WMC1501. Se il progetto considera gli avvisi come errori, questi avvisi arrestano la compilazione.
Dopo aver deciso di usare le API sperimentali, è possibile sopprimere questi avvisi. Aggiungere le proprietà seguenti al file di progetto C# (con estensione csproj):
<PropertyGroup>
<NoWarn>$(NoWarn);CS8305</NoWarn>
<MSBuildWarningsAsMessages>$(MSBuildWarningsAsMessages);WMC1501</MSBuildWarningsAsMessages>
</PropertyGroup>
Gli esempi in questo articolo usano C#. Ogni esempio completo mostra il codice XAML per una pagina e la classe code-behind della pagina, MainPage.
Creare un'area di disegno penna
![]()
L'app Raccolta WinUI 3 include esempi interattivi di controlli e funzionalità WinUI. Ottenere l'app dal Microsoft Store o esplorare il codice sorgente in GitHub.
Nell'esempio seguente viene inserita un'area di disegno all'interno di un elemento bianco Border. Il code-behind imposta i tipi di dispositivo di input in modo che gli utenti possano disegnare con penna, mouse o tocco.
<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;
}
}
Per mantenerli brevi, alcuni esempi successivi non ripetono questo codice. Impostare InputDeviceTypes in ogni pagina in cui gli utenti disegnano con il mouse.
Connetti una barra degli strumenti penna
Posizionare un oggetto InkToolbar vicino all'area di disegno e impostarne la TargetInkCanvas proprietà per connettere i due elementi:
<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>
A partire da SDK per app di Windows sperimentale 2.5.4, è invece possibile connettere una barra degli strumenti direttamente a un InkPresenter impostando la TargetInkPresenter proprietà . Impostarlo nel codice:
inkToolbar.TargetInkPresenter = inkCanvas.InkPresenter;
Puoi anche impostarlo in XAML: TargetInkPresenter="{x:Bind inkCanvas.InkPresenter}". Impostare solo una delle due proprietà di destinazione.
Per impostazione predefinita, la barra degli strumenti mostra i pulsanti penna, matita, evidenziatore, gomma e stencil (righello). Per modificare i pulsanti visualizzati, vedere Personalizzare la barra degli strumenti input penna.
Scegli i dispositivi di input
La InputDeviceTypes proprietà usa l'enumerazione CoreInputDeviceTypes flag dello Windows.UI.Core spazio dei nomi . L'input del mouse non consente di tracciare input penna per impostazione predefinita. Combinare i valori per scegliere quali dispositivi tracciare l'input penna:
// 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);
Per interrompere la raccolta dell'inchiostro da ogni dispositivo, ad esempio per mostrare un disegno che gli utenti non possono modificare, impostare IsInputEnabled su false:
inkCanvas.InkPresenter.IsInputEnabled = false;
Per consentire agli utenti di attivare o disattivare un dispositivo di input dalla barra degli strumenti, vedere Aggiungere un pulsante personalizzato di attivazione/disattivazione.
Impostare gli attributi di disegno
Gli attributi di disegno, ad esempio il colore, le dimensioni e la forma della punta della penna, controllano l'aspetto dei tratti. Usano la classe Windows RuntimeInkDrawingAttributes. Per modificare gli attributi per i nuovi tratti, chiamare CopyDefaultDrawingAttributes, modificare la copia e passarla a UpdateDefaultDrawingAttributes:
InkDrawingAttributes attributes = inkCanvas.InkPresenter.CopyDefaultDrawingAttributes();
attributes.Color = Colors.DarkBlue;
attributes.Size = new Size(4, 4);
inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(attributes);
Colors è nel namespace Microsoft.UI e Size si trova nel namespace Windows.Foundation. I nuovi attributi si applicano ai tratti che gli utenti disegnano dopo la chiamata della funzione. I tratti esistenti mantengono gli attributi.
Quando si collega InkToolbar, lo strumento attivo e le relative impostazioni di colore e dimensioni determinano gli attributi di disegno. Per leggere gli attributi correnti, utilizzare la proprietà InkDrawingAttributes della barra degli strumenti.
Crea preimpostazioni penna
Se non si usa una barra degli strumenti, è possibile definire i propri preset della penna. Nell'esempio seguente vengono applicati attributi penna a sfera, matita, evidenziatore o calligrafia quando l'utente seleziona un predefinito o modifica le dimensioni.
<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.CreateForPencilrestituisce gli attributi per i tratti della matita. La proprietàKindè di sola lettura, quindi usare questo metodo per creare attributi della matita. -
DrawAsHighlightervisualizza il tratto come sovrapposizione evidenziatore. -
PenTipimposta la forma della punta della penna ePenTipTransformruota o la ridimensiona. Una punta rettangolare ruotata produce tratti di stile calligrafia.
Rispondere alle modifiche all'input penna
Il presenter attiva StrokesCollected dopo che un utente disegna i tratti e StrokesErased dopo che un utente li cancella. La Strokes proprietà degli argomenti di ogni evento contiene i tratti interessati. Il codice seguente mantiene aggiornato il conteggio delle pressioni dei tasti in un TextBlock oggetto denominato 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";
}
Questi eventi si verificano quando gli utenti disegnano o cancellano l'input penna. Quando si modificano le tracce nel codice, ad esempio con Clear, LoadAsync, o AddStroke, aggiornare l'interfaccia utente dopo la chiamata.
Salva e carica l'inchiostro
InkStrokeContainer salva l'inchiostro nel formato ISF (Ink Serialized Format). Per scegliere la modalità di archiviazione dei dati, passare un InkPersistenceFormat valore a SaveAsync:
| Format | Output |
|---|---|
GifWithEmbeddedIsf |
Immagine GIF che contiene i dati di input penna. I visualizzatori di immagini possono visualizzare il file e l'app può caricare nuovamente l'inchiostro digitale per la modifica. |
Isf |
Solo i dati di input penna digitale, senza un'immagine. Questo è il formato più compatto. |
Aggiungere i metodi seguenti al code-behind di una pagina contenente un InkCanvas oggetto denominato inkCanvas.
SaveInkAsync scrive il tratto penna in un file e LoadInkAsync sostituisce il tratto penna nel canvas con il tratto penna da un file.
LoadAsync rimuove i tratti esistenti prima di aggiungere i tratti caricati, quindi non è necessario chiamare Clear prima.
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 e AsInputStream convertono i flussi .NET nei tipi di flusso di Windows Runtime che SaveAsync e LoadAsync usano. Per attendere questi metodi di Windows Runtime, il file richiede la using System; direttiva . Senza di esso, il compilatore segnala l'errore CS4036.
Per consentire agli utenti di scegliere un file, usare i selettori file di SDK per app di Windows, che restituiscono il percorso del file selezionato. Per altre informazioni, vedere Salvare un file con un selettore di SDK per app di Windows e Aprire file e cartelle con i selettori di SDK per app di Windows.
Per mantenere l'input penna in memoria, ad esempio per ripristinarlo in un secondo momento nella stessa sessione, salvarlo in un InMemoryRandomAccessStream. Passa il risultato del flusso GetOutputStreamAt(0) a SaveAsync e passarne il risultato GetInputStreamAt(0) a LoadAsync. L'esempio WinUI 3 Gallery usa questo approccio.
Copia e incolla l'input penna digitale
InkStrokeContainer può copiare i tratti negli Appunti e incollarvi l'input penna.
CopySelectedToClipboard copia solo i tratti selezionati, quindi nell'esempio seguente viene selezionato ogni tratto nell'area di disegno sinistra prima di copiarlo.
CanPasteFromClipboard controlla se gli Appunti contengono input penna digitale.
PasteFromClipboard aggiunge l'inchiostro digitale nella posizione passata e restituisce il rettangolo di delimitazione dell'area modificata.
<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.";
}
}
Per utilizzare alcuni tratti, impostare la Selected proprietà di ogni tratto desiderato oppure chiamare SelectWithLine o SelectWithPolyLine. Per eliminare o spostare i tratti selezionati, usare DeleteSelected o MoveSelected.
Creare tracciati nel codice
Usare la classe Windows Runtime InkStrokeBuilder per creare tratti da punti e aggiungerli all'area di disegno con AddStroke. Nell'esempio seguente viene aggiunta una stella e una riga di firma.
<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);
}
}
-
CreateStrokeFromInkPointscrea un tratto a partire dai valoriInkPoint. Ogni punto combina una posizione nell'area di disegno con un valore di pressione. - La documentazione di Windows Runtime consiglia
CreateStrokeFromInkPointsinvece diCreateStrokeper i tratti aggiunti a un presentatore input penna. - Quando
FitToCurveèfalse, il tratto usa segmenti dritti tra punti anziché curve.
Un tratto può appartenere a un solo contenitore. Per copiare i tratti in un'altra area di disegno, aggiungere cloni dei tratti:
private void CopyStrokesTo(InkCanvas target)
{
foreach (InkStroke stroke in inkCanvas.InkPresenter.StrokeContainer.GetStrokes())
{
target.InkPresenter.StrokeContainer.AddStroke(stroke.Clone());
}
}
Personalizza la barra degli strumenti della penna
Scegliere i controlli iniziali
La proprietà InitialControls determina quali pulsanti predefiniti crea la barra degli strumenti:
| Value | Pulsanti integrati |
|---|---|
All (impostazione predefinita) |
Penna a sfera, matita, evidenziatore, gomma e stencil (righello) |
PensOnly |
Penna a sfera, matita ed evidenziatore |
AllExceptPens |
Gomma e sagoma (righello) |
None |
None. Dichiarare i pulsanti desiderati come elementi figlio della barra degli strumenti. |
L'esempio seguente dichiara gli stessi quattro pulsanti dell'esempio della WinUI 3 Gallery, assegna a ognuno un nome accessibile e seleziona la penna a sfera quando la barra degli strumenti viene caricata:
<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;
}
};
}
}
Per visualizzare un comando che cancella tutto l'input penna, impostare la proprietà del IsClearAllVisible pulsante gomma su true. La barra degli strumenti genera l'evento EraseAllClicked quando l'utente seleziona il comando.
Personalizza i colori e lo spessore di una penna
Ogni pulsante della penna dispone di una serie Palette di pennelli e impostazioni di larghezza del tratto. Imposta Palette nel codice. Per informazioni dettagliate, vedere Limitazioni note. Nell'esempio seguente viene sostituita la gamma di colori della penna a sfera con quattro colori del marchio, viene ridotta la larghezza del tratto e vengono visualizzati gli attributi della penna attiva.
<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 seleziona un colore in Palette.
SelectedStrokeWidth, MinStrokeWidth e MaxStrokeWidth impostano la larghezza del tratto corrente e la gamma tra cui gli utenti possono scegliere. La barra degli strumenti genera InkDrawingAttributesChanged quando cambiano gli attributi dello strumento attivo.
Selezionare lo strumento attivo
Impostare ActiveTool per selezionare uno strumento e il gestore ActiveToolChanged per rispondere quando l'utente seleziona un altro strumento.
GetToolButton restituisce un pulsante strumento predefinito del tipo passato. I pulsanti predefiniti potrebbero non esistere fino al caricamento della barra degli strumenti, quindi selezionare uno strumento iniziale nel gestore eventi della Loaded barra degli strumenti.
<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}";
}
}
Modifica l'orientamento
Per visualizzare la barra degli strumenti verticalmente, ad esempio accanto all'area di disegno, impostare Orientation su 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 proprietà ButtonFlyoutPlacement definisce dove i menu a comparsa del pulsante si aprono rispetto alla barra degli strumenti. Il valore predefinito, Auto, consente alla barra degli strumenti di scegliere la posizione.
Aggiungere un pulsante di attivazione/disattivazione personalizzato
Usare un InkToolbarCustomToggleButton per aggiungere un'impostazione che gli utenti possono attivare e disattivare dalla barra degli strumenti. Nell'esempio seguente viene aggiunto un interruttore che controlla l'input tattile. L'input da penna e mouse disegna sempre inchiostro.
<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}";
}
}
L'interruttore mostra solo un'icona, quindi l'esempio assegna un nome accessibile e una descrizione comando. In questo esempio, la barra degli strumenti crea anche i pulsanti predefiniti e l'interruttore personalizzato viene visualizzato dopo di essi.
La barra degli strumenti supporta altri pulsanti personalizzati:
-
InkToolbarCustomToolButtonaggiunge uno strumento creato da te. -
InkToolbarCustomPenButtonaggiunge uno stilo che crea i propri attributi di disegno. Impostare la relativaCustomPenproprietà su un'istanza di una classe che deriva daInkToolbarCustomPened esegue l'override delCreateInkDrawingAttributesCoremetodo .
Creare la barra degli strumenti nel codice
È anche possibile creare l'area di disegno e la barra degli strumenti nel codice. L'esempio seguente li aggiunge a un Border oggetto denominato inkHost, connette la barra degli strumenti con TargetInkPresentere seleziona la matita quando viene caricata la barra degli strumenti.
<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;
}
}
Limitazioni note
- I controlli di input penna sono disponibili solo nelle versioni sperimentali del SDK per app di Windows. Non sono disponibili nel canale stabile.
- Le API di inchiostro digitale non includono il riconoscimento della grafia o della forma.
- L'input del mouse non traccia inchiostro finché non si aggiunge
CoreInputDeviceTypes.MouseaInputDeviceTypes.
I problemi seguenti sono stati segnalati con SDK per app di Windows sperimentale 2.4.1. Le note sulla versione sperimentale 2.5.4 non li elencano come risolti:
- Gli stencil del righello e del goniometro non funzionano.
-
RenderTargetBitmapnon acquisisce l'inchiostro. Per creare un'immagine dell'inchiostro, salvarla in formatoGifWithEmbeddedIsf. - L'impostazione della proprietà
Palettedi un pulsante penna in XAML causa un errore di analisi XAML. ImpostarePaletteinvece nel codice, come illustrato in Personalizzare i colori e le larghezze di una penna. - I riquadri a comparsa dei pulsanti possono essere vuoti e modificando
ButtonFlyoutPlacementpotrebbe non cambiare la posizione in cui vengono aperti.
Modifiche dalla versione 2.4.1-experimental
Se hai usato le API di input penna in SDK per app di Windows 2.4.1-experimental, aggiorna il codice per queste modifiche nella versione 2.5.4-experimental:
| API in 2.4.1-experimental | Cambiamento in 2.5.4-sperimentale |
|---|---|
InkToolbar.TargetInkPresenter (tipo Object) |
Il tipo di proprietà è InkPresenter. |
metodi InkPresenter.GetHighContrastAdjustment e InkPresenter.SetHighContrastAdjustment |
Sostituito dalla proprietà InkPresenter.HighContrastAdjustment. |
metodi InkStrokeInput.GetInkPresenter e InkUnprocessedInput.GetInkPresenter |
Sostituito da una proprietà InkPresenter su ciascuna classe. |
InkToolbarEraserButton.SelectedEraser, ArePrecisionErasersVisible e InkToolbar.EraserFlyoutItemClicked proprietà; IsStrokeEraserVisible evento; InkToolbarEraserKind, InkToolbarEraserFlyoutItemKind e InkToolbarEraserFlyoutItemClickedEventArgs tipi |
Rimosso. |
| Non disponibile |
InkPresenter.ActivateCustomDrying metodo e InkSynchronizer classe aggiunti. |
Accessibilità e contrasto elevato
L'inchiostro digitale è contenuto visivo, quindi pianificare come tutti gli utenti possono usare le funzionalità di input penna:
- Assegnare a ogni area di disegno e barra degli strumenti un nome accessibile con
AutomationProperties.Name, come gli esempi in questo articolo. Assegna ai pulsanti personalizzati che mostrano solo un'icona un nome e una descrizione comando. -
InkCanvasAutomationPeerespone l'area di disegno a Automazione interfaccia utente, ma non descrive i tratti. Se l'inchiostro ha un significato, ad esempio una firma o un diagramma etichettato, fornire un'alternativa testuale. - L'input penna richiede un puntatore. Non rendere l'input tramite penna l'unico modo per completare un'attività. Ad esempio, consentire agli utenti di digitare il nome come alternativa alla firma.
- I peer di automazione per i pulsanti degli strumenti predefiniti supportano i pattern di controllo SelectionItem e ExpandCollapse. Testare lo spostamento tramite tastiera e i riquadri a comparsa del pulsante con la configurazione della barra degli strumenti usata dall'app.
Quando un tema a contrasto è attivo, il presentatore può sostituire i colori dell'inchiostro con i colori di sistema. Per scegliere questo comportamento, impostare la proprietà HighContrastAdjustment su un valore InkHighContrastAdjustment del namespace Microsoft.UI.Xaml.Controls. La documentazione Windows Runtime per l'enumerazione con lo stesso nome descrive i valori in questo modo:
| Value | Colore dell'inchiostro in un tema a contrasto |
|---|---|
UseSystemColorsWhenNecessary |
Il colore dell'inchiostro se contrasta abbastanza con lo sfondo. In caso contrario, un colore di sistema. |
UseSystemColors |
Colore di sistema. L'inchiostro dell'evidenziatore usa il colore dell'evidenziatore di sistema. |
UseOriginalColors |
Colore dell'inchiostro. |
inkCanvas.InkPresenter.HighContrastAdjustment = InkHighContrastAdjustment.UseSystemColorsWhenNecessary;
Per mantenerli brevi, gli esempi in questo articolo impostano direttamente uno sfondo bianco. Nell'app definire lo sfondo dell'area di disegno nei dizionari dei temi in modo che segua il tema attivo, come descritto in Temi a contrasto. Testare quindi l'app con ogni tema a contrasto e verificare che l'inchiostro, lo sfondo e il bordo rimangano distinguibili.
Per altre informazioni, vedere Esporre informazioni di accessibilità di base e Accessibilità tramite tastiera.
Vedere anche
- Codice sorgente dell'esempio InkCanvas della raccolta WinUI 3
- Note sulla versione sperimentale di SDK per app di Windows 2.5.4
- namespace Microsoft.UI.Xaml.Controls
- namespace Windows.UI.Input.Inking
- Feedback aptico della penna nelle app Windows
- Interazioni con penna e Windows Ink (UWP)
- Controlli di input penna (UWP)
- Cosa è supportato durante la migrazione da UWP a WinUI 3