Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Important
InkCanvas, InkToolbarund InkPresenter sind experimentelle APIs. Die Freihandunterstützung wurde in Windows App SDK 2.4.1-experimentell hinzugefügt. Dieser Artikel beschreibt das Windows App SDK 2.5.4-experimental, das die Art und Weise ändert, wie Sie ein InkToolbar verbinden. Diese APIs sind im stabilen Kanal nicht verfügbar. Sie können in einer zukünftigen Version geändert oder entfernt werden. Verwenden Sie sie nicht in Produktions-Apps.
Verwenden Sie das experimentelle InkCanvas Steuerelement, damit Benutzer Freihand mit einem Stift, einer Maus oder per Touch zeichnen können. Fügen Sie eine InkToolbar hinzu, um Benutzern einen Kugelschreiber, einen Bleistift, einen Textmarker und einen Radiergummi mit Farb- und Größenoptionen zur Verfügung zu stellen. Jeder Zeichenbereich verfügt über ein InkPresenter, das Freihandeingaben erfasst und rendert. Die InkStrokeContainer des Presenters speichert die Striche, sodass Sie Freihandeingaben im Code speichern, laden, kopieren und erstellen können.
Um die Steuerelemente zu testen, installieren Sie das Windows App SDK 2.5.4-experimentelle Paket und die entsprechende Laufzeit. Downloadlinks und Setupinformationen finden Sie unter Neueste Windows App SDK Downloads.
Ist dies das richtige Steuerelement?
Verwenden Sie einen Freihandzeichenbereich, wenn Benutzer Folgendes benötigen:
- Erstellen Sie handschriftliche Notizen oder versehen Sie Inhalte mit Anmerkungen.
- Skizzieren, Diagramme zeichnen oder den eigenen Namen unterschreiben.
- Erstellen Sie Freihandinhalte, die Ihre App speichert und die Benutzer später bearbeiten können.
Die experimentellen Freihand-APIs enthalten keine Handschrift- oder Formerkennung. Verwenden Sie zur Eingabe von Text ein Textfeld.
Wenn Sie Freihandeingaben in einer Produktions-App benötigen, finden Sie unter Bekannte Lücken bei Steuerelementen Informationen zum Status dieser Steuerelemente im Stable-Kanal sowie zu Alternativen.
Grundlegendes zum Freihandmodell
Die Freihand-APIs arbeiten zusammen:
| API | Role |
|---|---|
InkCanvas |
Das XAML-Element, das Freihandeingaben anzeigt und empfängt. Die zugehörige InkPresenter-Eigenschaft gibt den Presenter für die Canvas zurück. |
InkPresenter |
Erfasst Freihandeingaben, rendert Striche und enthält die Eingabeeinstellungen und Standardzeichnungsattribute. Sie können InkPresenter nicht selbst erstellen. Rufen Sie ihn von InkCanvas.InkPresenter ab. |
InkStrokeContainer |
Speichert Striche. Verwenden Sie ihn, um Freihandeingaben zu speichern, zu laden, auszuwählen, zu kopieren und einzufügen. Rufen Sie ihn von InkPresenter.StrokeContainer ab. |
InkToolbar |
Eine optionale Symbolleiste, mit der Benutzer einen Stift, Bleistift, Textmarker oder Radierer auswählen und Freihandfarbe und -größe ändern können. |
InkCanvas verfügt nicht über eine Background Eigenschaft. Um dem Zeichnungsbereich einen Hintergrund und einen Rahmen zu verleihen, platzieren Sie das Canvas in einem Container, z. B. einem BorderContainer, und legen Sie die Größe auf dem Container fest. Die Standardfarbe für Tinte ist schwarz, daher verwenden die Beispiele in diesem Artikel einen weißen Hintergrund.
Mehrere dieser Typen haben dieselben Namen wie Typen im Windows-Runtime-Windows.UI.Input.Inking-Namespace, z. B. InkPresenter, InkStrokeContainer und InkHighContrastAdjustment. Die Freihandsteuerelemente verwenden die Microsoft.UI.Xaml.Controls Versionen. Striche und Zeichnungsattribute verwenden weiterhin Windows-Runtime-Typen, z. B. InkStroke, InkDrawingAttributes und InkStrokeBuilder. Wenn Sie beide Namespaces importieren und einen mehrdeutigen Namen verwenden, meldet der C#-Compiler den Fehler CS0104.
In den Beispielen in diesem Artikel wird Windows.UI.Input.Inking importiert, und es werden Aliasdirektiven für die benötigten Microsoft.UI.Xaml.Controls-Typen verwendet:
using Microsoft.UI.Xaml.Controls;
using InkDrawingAttributes = Windows.UI.Input.Inking.InkDrawingAttributes;
using InkStroke = Windows.UI.Input.Inking.InkStroke;
Sie können auch sowohl Namespaces importieren als auch vollqualifizierte Namen für die mehrdeutigen Typen verwenden, wie es das WinUI 3 Gallery-Beispiel vormacht.
InkPresenter enthält auch APIs auf niedrigerer Ebene, die in diesem Artikel nicht behandelt werden:
-
StrokeInputlöst Ereignisse aus, wenn ein Strich beginnt, fortgesetzt, beendet oder abgebrochen wird. -
UnprocessedInputlöst Zeigerereignisse für Eingaben aus, die der Presenter nicht als Freihand verarbeitet. -
InputConfigurationlegt fest, ob der Presenter Eingaben vom Radiergummi und der primären Schafttaste eines Stifts verarbeitet. -
InputProcessingConfigurationlegt fest, ob die Eingabe Freihand zeichnet, Freihand löscht oder nicht verarbeitet wird und wie der Presenter das Ziehen mit der rechten Maustaste behandelt. -
ActivateCustomDryinggibt einInkSynchronizerfür Apps zurück, die committete Freihandeingaben selbst rendern.
Einrichten Ihres Projekts
Die Freihand-APIs sind experimentell markiert. Wenn Sie sie in einem C#-Projekt verwenden, meldet der C#-Compiler die Warnung CS8305 und der XAML-Compiler meldet Warnungen WMC1501. Wenn Ihr Projekt Warnungen als Fehler behandelt, beenden diese Warnungen den Build.
Nachdem Sie sich für die Verwendung der experimentellen APIs entschieden haben, können Sie diese Warnungen unterdrücken. Fügen Sie der C#-Projektdatei (CSPROJ) die folgenden Eigenschaften hinzu:
<PropertyGroup>
<NoWarn>$(NoWarn);CS8305</NoWarn>
<MSBuildWarningsAsMessages>$(MSBuildWarningsAsMessages);WMC1501</MSBuildWarningsAsMessages>
</PropertyGroup>
In den Beispielen in diesem Artikel wird C# verwendet. Jedes vollständige Beispiel zeigt den XAML-Code für eine Seite und die CodeBehind-Klasse der Seite. MainPage
Erstellen einer Freihand-Canvas
![]()
Die WinUI 3 Gallery-App enthält interaktive Beispiele für WinUI-Steuerelemente und -Features. Rufen Sie die App aus dem Microsoft Store ab, oder durchsuchen Sie den Quellcode auf GitHub.
Im folgenden Beispiel wird eine Canvas in einem weißen Border platziert. Der CodeBehind legt die Eingabegerätetypen fest, sodass Benutzer mit einem Stift, einer Maus oder einer Toucheingabe zeichnen können.
<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;
}
}
Um sie kurz zu halten, wiederholen einige spätere Beispiele diesen Code nicht. Legen Sie InputDeviceTypes auf jeder Seite fest, auf der Benutzer mit einer Maus zeichnen.
Verbinden einer Freihandsymbolleiste
Platzieren Sie ein InkToolbar in der Nähe des Zeichenbereichs, und legen Sie dessen TargetInkCanvas-Eigenschaft fest, um die beiden zu verbinden:
<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>
Ab Windows App SDK 2.5.4-experimental können Sie stattdessen eine Symbolleiste direkt mit einer InkPresenter verbinden, indem Sie die TargetInkPresenter Eigenschaft festlegen. Legen Sie sie im Code fest:
inkToolbar.TargetInkPresenter = inkCanvas.InkPresenter;
Sie können sie auch in XAML festlegen: TargetInkPresenter="{x:Bind inkCanvas.InkPresenter}". Legen Sie nur eine der beiden Zieleigenschaften fest.
Standardmäßig werden auf der Symbolleiste Schaltflächen für Kugelschreiber, Bleistift, Textmarker, Radierer und Schablone (Lineal) angezeigt. Informationen zum Ändern der angezeigten Schaltflächen finden Sie unter Anpassen der Freihandsymbolleiste.
Eingabegeräte auswählen
Die InputDeviceTypes Eigenschaft verwendet die CoreInputDeviceTypes Flags-Aufzählung aus dem Windows.UI.Core Namespace. Mit der Mauseingabe wird standardmäßig keine Freihandzeichnung erzeugt. Kombinieren Sie Werte, um auszuwählen, welche Geräte Freihandeingaben zeichnen:
// 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);
Wenn Sie das Sammeln von Freihandeingaben von jedem Gerät beenden möchten, um beispielsweise eine Zeichnung anzuzeigen, die Benutzer nicht ändern können, legen Sie IsInputEnabled auf false fest:
inkCanvas.InkPresenter.IsInputEnabled = false;
Informationen dazu, wie Benutzer ein Eingabegerät über die Symbolleiste ein- oder ausschalten können, finden Sie unter Hinzufügen einer benutzerdefinierten Umschaltfläche.
Zeichnungsattribute festlegen
Zeichenattribute wie Farbe, Größe und Form der Stiftspitze steuern, wie Striche aussehen. Sie verwenden die Windows-Runtime InkDrawingAttributes Klasse. Um die Attribute für neue Striche zu ändern, rufen Sie CopyDefaultDrawingAttributesauf, ändern Sie die Kopie, und übergeben Sie sie an UpdateDefaultDrawingAttributes:
InkDrawingAttributes attributes = inkCanvas.InkPresenter.CopyDefaultDrawingAttributes();
attributes.Color = Colors.DarkBlue;
attributes.Size = new Size(4, 4);
inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(attributes);
Colors befindet sich im Microsoft.UI Namespace und Size befindet sich im Windows.Foundation Namespace. Die neuen Attribute gelten für Striche, die Benutzer nach dem Aufruf zeichnen. Vorhandene Striche behalten ihre Attribute bei.
Wenn Sie InkToolbar anschließen, bestimmen das aktive Tool und dessen Farb- und Größeneinstellungen die Zeichnungsattribute. Verwenden Sie die InkDrawingAttributes-Eigenschaft der Symbolleiste, um die aktuellen Attribute zu lesen.
Erstellen von Stiftvoreinstellungen
Wenn Sie keine Symbolleiste verwenden, können Sie eigene Stiftvoreinstellungen anbieten. Im folgenden Beispiel werden Ballpoint-, Bleistift-, Textmarker- oder Calligraphieattribute angewendet, wenn der Benutzer eine Voreinstellung auswählt oder die Größe ändert.
<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.CreateForPencilgibt Attribute für Bleistiftstriche zurück. DieKindEigenschaft ist schreibgeschützt. Verwenden Sie daher diese Methode, um Bleistiftattribute zu erstellen. -
DrawAsHighlighterrendert den Strich als Textmarker-Überlagerung. -
PenTiplegt die Form der Stiftspitze fest undPenTipTransformdreht oder skaliert sie. Eine gedrehte rechteckige Spitze erzeugt Striche im Kalligrafiestil.
Reagieren auf Freihandänderungen
Der Presenter löst StrokesCollected aus, nachdem ein Benutzer Striche zeichnet, und StrokesErased nachdem ein Benutzer sie löscht. Die Strokes-Eigenschaft der Argumente jedes Ereignisses enthält die betroffenen Striche. Der folgende Code hält die Anzahl der Striche in einem TextBlock mit dem Namen strokeCountText auf dem neuesten Stand:
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";
}
Diese Ereignisse treten auf, wenn Benutzer Freihand zeichnen oder Freihandstriche löschen. Wenn Sie Striche im Code ändern, z. B. mit Clear, LoadAsyncoder AddStrokeaktualisieren Sie die Benutzeroberfläche nach dem Aufruf.
Speichern und Laden von Freihandeingaben
InkStrokeContainer speichert Freihandeingaben im ISF-Format (Ink Serialized Format). Um auszuwählen, wie die Daten gespeichert werden sollen, übergeben Sie einen InkPersistenceFormat Wert an SaveAsync:
| Format | Output |
|---|---|
GifWithEmbeddedIsf |
Ein GIF-Bild, das die Inkdaten enthält. Bildbetrachter können die Datei anzeigen, und Ihre App kann die Freihanddaten erneut zur Bearbeitung laden. |
Isf |
Nur Freihanddaten, ohne Bild. Dies ist das kompakteste Format. |
Fügen Sie die folgenden Methoden zum CodeBehind einer Seite hinzu, die ein InkCanvas mit dem Namen inkCanvas enthält.
SaveInkAsync schreibt die Freihandeingabe in eine Datei und LoadInkAsync ersetzt die Freihandeingabe auf dem Zeichenbereich durch die Freihandeingabe aus einer Datei.
LoadAsync entfernt die vorhandenen Striche, bevor die geladenen Striche hinzugefügt werden, sodass Sie Clear nicht zuerst aufrufen müssen.
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 und AsInputStream konvertieren .NET-Streams in die Windows-Runtime-Streamtypen, die von SaveAsync und LoadAsync verwendet werden. Um auf diese Windows-Runtime Methoden zu warten, benötigt die Datei die using System; Direktive. Ohne dies meldet der Compiler den Fehler CS4036.
Um Benutzern die Auswahl einer Datei zu ermöglichen, verwenden Sie die Windows App SDK Dateiauswahldialoge, die den Pfad der ausgewählten Datei zurückgeben. Weitere Informationen finden Sie unter Speichern einer Datei mit einer Windows App SDK Auswahl und Öffnen von Dateien und Ordnern mit Windows App SDK Auswahl.
Um Freihandeingaben im Arbeitsspeicher zu behalten, z. B. um sie später in derselben Sitzung wiederherzustellen, speichern Sie sie in einem InMemoryRandomAccessStream. Übergeben Sie das Ergebnis des Datenstroms GetOutputStreamAt(0) an SaveAsync, und übergeben Sie sein GetInputStreamAt(0)-Ergebnis an LoadAsync. Im Beispiel "WinUI 3 Gallery" wird dieser Ansatz verwendet.
Kopieren und Einfügen von Freihandeingaben
InkStrokeContainer kann Striche in die Zwischenablage kopieren und Freihandeingaben daraus einfügen.
CopySelectedToClipboard kopiert nur ausgewählte Striche, sodass im folgenden Beispiel alle Striche im linken Zeichenbereich ausgewählt werden, bevor sie kopiert werden.
CanPasteFromClipboard überprüft, ob die Zwischenablage Freihandeingaben enthält.
PasteFromClipboard fügt die Freihandeingabe an der übergebenen Position hinzu und gibt das umgebende Rechteck des geänderten Bereichs zurück.
<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.";
}
}
Um mit einigen der Striche zu arbeiten, legen Sie die Selected-Eigenschaft jedes gewünschten Strichs fest, oder rufen Sie SelectWithLine oder SelectWithPolyLine auf. Um die ausgewählten Striche zu löschen oder zu verschieben, rufen Sie DeleteSelected oder MoveSelected auf.
Erstellen von Strichen im Code
Verwenden Sie die Windows-Runtime InkStrokeBuilder Klasse, um Striche aus Punkten zu erstellen und sie mit AddStroke dem Zeichenbereich hinzuzufügen. Im folgenden Beispiel wird ein Stern und eine Signaturzeile hinzugefügt.
<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);
}
}
-
CreateStrokeFromInkPointserstellt eine Kontur ausInkPointWerten. Jeder Punkt kombiniert eine Position auf dem Zeichenbereich mit einem Druckwert. - In der Windows-Runtime-Dokumentation wird
CreateStrokeFromInkPointsanstelle vonCreateStrokefür Striche empfohlen, die Sie einem Freihand-Presenter hinzufügen. - Wenn
FitToCurveden Wertfalseaufweist, verwendet der Strich gerade Segmente zwischen Punkten anstelle von Kurven.
Ein Strich kann nur zu einem Container gehören. Um Striche in einen anderen Zeichenbereich zu kopieren, fügen Sie Klone der Striche hinzu:
private void CopyStrokesTo(InkCanvas target)
{
foreach (InkStroke stroke in inkCanvas.InkPresenter.StrokeContainer.GetStrokes())
{
target.InkPresenter.StrokeContainer.AddStroke(stroke.Clone());
}
}
Freihandsymbolleiste anpassen
Wählen Sie die initialen Bedienelemente aus
Die InitialControls-Eigenschaft bestimmt, welche integrierten Schaltflächen die Symbolleiste erstellt:
| Wert | Integrierte Schaltflächen |
|---|---|
All (Standardwert) |
Kugelschreiber, Bleistift, Textmarker, Radiergummi und Schablone (Lineal) |
PensOnly |
Kugelschreiber, Bleistift und Textmarker |
AllExceptPens |
Radiergummi und Schablone (Lineal) |
None |
None. Deklarieren Sie die gewünschten Schaltflächen als untergeordnete Elemente der Symbolleiste. |
Im folgenden Beispiel werden dieselben vier Schaltflächen wie im WinUI 3-Katalogbeispiel deklariert, jeder ein barrierefreier Name zugewiesen und der Kugelschreiber ausgewählt, wenn die Symbolleiste geladen wird:
<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;
}
};
}
}
Um einen Befehl anzuzeigen, der alle Freihandeingaben löscht, legen Sie die Eigenschaft der IsClearAllVisible-Radiererschaltfläche auf den Wert true fest. Die Symbolleiste löst das EraseAllClicked Ereignis aus, wenn der Benutzer den Befehl auswählt.
Farben und Strichstärken eines Stifts anpassen
Jede Stiftschaltfläche verfügt über eine Palette mit Pinseln und Einstellungen für die Strichstärke. Legen Sie Palette im Code fest. Ausführlichere Informationen finden Sie unter Bekannte Einschränkungen. Im folgenden Beispiel wird die Palette eines Kugelschreibers durch vier Markenfarben ersetzt, die Strichbreite begrenzt und die Attribute des aktiven Stifts angezeigt.
<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: Wählt eine Farbe aus Palette.
SelectedStrokeWidth, MinStrokeWidthund MaxStrokeWidth legen Sie die aktuelle Strichbreite und den Bereich fest, aus dem Benutzer wählen können. Die Symbolleiste löst InkDrawingAttributesChanged aus, wenn sich die Attribute des aktiven Tools ändern.
Aktives Werkzeug auswählen
Legen Sie ActiveTool fest, um ein Tool auszuwählen, und behandeln Sie ActiveToolChanged, um zu reagieren, wenn der Benutzer ein anderes auswählt.
GetToolButton gibt eine integrierte Toolschaltfläche der übergebenen Art zurück. Die integrierten Schaltflächen existieren möglicherweise erst, wenn die Symbolleiste geladen wird. Wählen Sie daher im Ereignishandler Loaded der Symbolleiste ein Starttool aus.
<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}";
}
}
Ausrichtung ändern
Um die Symbolleiste vertikal anzuzeigen, z. B. neben dem Zeichenbereich, setzen Sie Orientation auf 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>
Die ButtonFlyoutPlacement Eigenschaft legt fest, wo Schaltflächen-Flyouts relativ zur Symbolleiste geöffnet werden. Mit dem Standardwert Autokann die Symbolleiste die Position auswählen.
Hinzufügen einer benutzerdefinierten Umschaltfläche
Verwenden Sie InkToolbarCustomToggleButton, um eine Einstellung hinzuzufügen, die Benutzer über die Symbolleiste aktivieren und deaktivieren können. Im folgenden Beispiel wird eine Umschaltfläche hinzugefügt, die die Toucheingabe steuert. Stift- und Mauseingaben erzeugen immer Freihandstriche.
<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}";
}
}
Die Umschaltfläche zeigt nur ein Symbol an, sodass das Beispiel einen barrierefreien Namen und eine QuickInfo enthält. In diesem Beispiel erstellt die Symbolleiste auch die Standardschaltflächen, und der benutzerdefinierte Umschalter wird danach angezeigt.
Die Symbolleiste unterstützt andere benutzerdefinierte Schaltflächen:
-
InkToolbarCustomToolButtonfügt ein eigenes Tool hinzu. -
InkToolbarCustomPenButtonfügt einen Stift hinzu, der über eigene Zeichnungsattribute verfügt. Legen Sie die EigenschaftCustomPenauf eine Instanz einer Klasse fest, die vonInkToolbarCustomPenabgeleitet ist und die MethodeCreateInkDrawingAttributesCoreüberschreibt.
Erstellen Sie die Symbolleiste im Code
Sie können auch den Zeichenbereich und die Symbolleiste im Code erstellen. Im folgenden Beispiel werden sie einem Border namens inkHost hinzugefügt, die Symbolleiste mit TargetInkPresenter verbunden und der Bleistift ausgewählt, wenn die Symbolleiste geladen wird.
<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;
}
}
Bekannte Einschränkungen
- Die Steuerelemente für Freihandeingaben sind nur in experimentellen Releases des Windows App SDK verfügbar. Sie sind im stabilen Kanal nicht verfügbar.
- Die Freihand-APIs enthalten keine Handschrift- oder Formerkennung.
- Mauseingaben zeichnen erst dann Freihandeingaben, wenn Sie
CoreInputDeviceTypes.MousezuInputDeviceTypeshinzufügen.
Die folgenden Probleme wurden mit Windows App SDK 2.4.1-experimentell gemeldet. In den Versionshinweisen zu 2.5.4-experimental werden sie nicht als behoben aufgeführt:
- Die Lineal- und Winkelmesserschablonen funktionieren nicht.
-
RenderTargetBitmaperfasst keine Freihandeingaben. Um ein Bild der Tinte zu erstellen, speichern Sie es imGifWithEmbeddedIsfFormat. - Das Festlegen der
PaletteEigenschaft einer Stiftschaltfläche in XAML führt zu einem XAML-Analysefehler. Legen SiePalettestattdessen im Code fest, wie in Farben und Breiten eines Stifts anpassen gezeigt. - Schaltflächen-Flyouts können leer angezeigt werden, und das Ändern von
ButtonFlyoutPlacementändert möglicherweise nicht, wo sie geöffnet werden.
Änderungen gegenüber 2.4.1-experimental
Wenn Sie die Freihand-APIs in Windows App SDK 2.4.1-experimental verwendet haben, aktualisieren Sie Ihren Code für diese Änderungen in 2.5.4-experimental:
| API in 2.4.1-experimental | Änderung in 2.5.4-experimental |
|---|---|
InkToolbar.TargetInkPresenter (Typ Object) |
Der Eigenschaftstyp ist InkPresenter. |
Die Methoden InkPresenter.GetHighContrastAdjustment und InkPresenter.SetHighContrastAdjustment |
Ersetzt durch die InkPresenter.HighContrastAdjustment Eigenschaft. |
Die Methoden InkStrokeInput.GetInkPresenter und InkUnprocessedInput.GetInkPresenter |
Ersetzt durch eine InkPresenter Eigenschaft in jeder Klasse. |
InkToolbarEraserButton.SelectedEraser, IsStrokeEraserVisible und InkToolbar.EraserFlyoutItemClicked Eigenschaften; InkToolbarEraserKind Ereignis; InkToolbarEraserFlyoutItemKind, InkToolbarEraserFlyoutItemClickedEventArgs und ArePrecisionErasersVisible Typen |
Entfernt. |
| Nicht verfügbar |
InkPresenter.ActivateCustomDrying Methode und InkSynchronizer Klasse hinzugefügt. |
Barrierefreiheit und hoher Kontrast
Freihand ist visueller Inhalt. Planen Sie daher, wie alle Benutzer Ihre Freihandfeatures verwenden können:
- Geben Sie jeder Canvas und Symbolleiste mit
AutomationProperties.Nameeinen barrierefreien Namen, wie in den Beispielen in diesem Artikel gezeigt. Geben Sie benutzerdefinierten Schaltflächen, die nur ein Symbol anzeigen, einen Namen und eine QuickInfo. -
InkCanvasAutomationPeermacht den Zeichenbereich für Benutzeroberflächenautomatisierung verfügbar, beschreibt jedoch nicht die Striche. Wenn die Freihandeingabe Bedeutung hat, z. B. eine Signatur oder ein beschriftetes Diagramm, stellen Sie eine Textalternative bereit. - Das Zeichnen von Freihandeingaben erfordert Zeigereingaben. Machen Sie Freihandeingaben nicht zur einzigen Möglichkeit, eine Aufgabe abzuschließen. Lassen Sie Benutzer beispielsweise ihren Namen als Alternative zum Unterschreiben eingeben.
- Die Automatisierungspeers für die integrierten Toolschaltflächen unterstützen das SelectionItem- und ExpandCollapse-Steuerelementmuster. Testen Sie die Tastaturnavigation und die Schaltflächen-Flyouts mit der symbolleistenkonfiguration, die Ihre App verwendet.
Wenn ein Kontrastdesign aktiv ist, kann der Referent Freihandfarben durch Systemfarben ersetzen. Um dieses Verhalten auszuwählen, legen Sie die HighContrastAdjustment Eigenschaft auf einen InkHighContrastAdjustment Wert aus dem Microsoft.UI.Xaml.Controls Namespace fest. Die Windows-Runtime Dokumentation für die Enumeration mit demselben Namen beschreibt die Werte auf diese Weise:
| Wert | Freihandfarbe in einem Kontrastdesign |
|---|---|
UseSystemColorsWhenNecessary |
Die eigene Farbe der Freihandeingabe, wenn sie sich ausreichend vom Hintergrund abhebt. Andernfalls eine Systemfarbe. |
UseSystemColors |
Eine Systemfarbe. Textmarker-Tinte verwendet die Farbe des System-Textmarkers. |
UseOriginalColors |
Die eigene Farbe der Tinte. |
inkCanvas.InkPresenter.HighContrastAdjustment = InkHighContrastAdjustment.UseSystemColorsWhenNecessary;
Um sie kurz zu halten, legen die Beispiele in diesem Artikel einen weißen Hintergrund direkt fest. Definieren Sie in Ihrer App den Canvashintergrund in Themenwörterbüchern, sodass er dem aktiven Thema folgt, wie in Kontrastthemen beschrieben. Testen Sie Ihre App dann mit jedem Kontrastdesign, und überprüfen Sie, ob die Freihandeingabe, der Hintergrund und der Rahmen unterscheidbar bleiben.
Weitere Informationen finden Sie unter Grundlegende Informationen zur Barrierefreiheit bereitstellen und Tastaturbedienbarkeit.
Siehe auch
- WinUI 3 Gallery InkCanvas-Beispielquellcode
- Windows App SDK 2.5.4-experimentelle Versionshinweise
- Microsoft.UI.Xaml.Controls Namespace
- Windows.UI.Input.Inking-Namespace
- Haptisches Stiftfeedback in Windows Apps
- Stiftinteraktionen und Windows Ink (UWP)
- Ink-Steuerelemente (UWP)
- Was bei der Migration von UWP zu WinUI 3 unterstützt wird
Windows developer