Inktbesturingselementen voor WinUI-apps

Important

InkCanvas, InkToolbaren InkPresenter zijn experimentele API's. Inktondersteuning is toegevoegd in Windows App SDK 2.4.1-experimental. In dit artikel wordt Windows App SDK 2.5.4-experimental beschreven, dat heeft gewijzigd hoe u een InkToolbar verbindt. Deze API's zijn niet beschikbaar in het stabiele kanaal. Ze kunnen in een toekomstige release worden gewijzigd of verwijderd. Gebruik ze niet in productie-apps.

Gebruik het experimentele besturingselement InkCanvas waarmee gebruikers met inkt kunnen tekenen met een pen, muis of via aanraking. Voeg een InkToolbar toe om gebruikers een balpen, potlood, markeerstift en gum te geven, met opties voor kleur en grootte. Elk canvas heeft een InkPresenter object waarmee inktinvoer wordt verzameld en weergegeven. De InkStrokeContainer van de presentator slaat de pennenstreken op, zodat u inkt in code kunt opslaan, laden, kopiëren en maken.

Als u de besturingselementen wilt proberen, installeert u het Windows App SDK 2.5.4-experimental package en de bijbehorende runtime. Zie Meest recente Windows App SDK-downloads voor downloadkoppelingen en installatiegegevens.

Is dit de juiste controle?

Gebruik een inktcanvas wanneer gebruikers het volgende moeten doen:

  • Maak handgeschreven notities of annoteer inhoud.
  • Schets, teken diagrammen of onderteken je naam.
  • Maak inhoud uit de vrije hand die uw app opslaat en die gebruikers later kunnen bewerken.

De experimentele inkt-API's bevatten geen handschrift- of vormherkenning. Als u tekst wilt verzamelen, gebruikt u een tekstvak.

Als u inken in een productie-app nodig hebt, raadpleegt u Bekende controle-hiaten voor de status van deze besturingselementen in het stabiele kanaal en voor alternatieven.

Begrijp het inktmodel

De ink-API's werken samen:

API Role
InkCanvas Het XAML-element dat inkt weergeeft en inktinvoer ontvangt. De InkPresenter eigenschap retourneert de presenter voor het canvas.
InkPresenter Verzamelt inktinvoer, rendert pennenstreken en bevat de invoerinstellingen en standaardtekeningkenmerken. U kunt niet zelf een InkPresenter maken. Download het van InkCanvas.InkPresenter.
InkStrokeContainer Slaat streken op. Gebruik deze om inkt op te slaan, te openen, te selecteren, te kopiëren en te plakken. Haal het op via InkPresenter.StrokeContainer.
InkToolbar Een optionele werkbalk waarmee gebruikers een pen, potlood, markeerstift of gum kunnen kiezen en inktkleur en -grootte kunnen wijzigen.

InkCanvas heeft geen Background eigenschap. Als u het tekengebied een achtergrond en een rand wilt geven, plaatst u het canvas in een container, zoals een Border, en stelt u de grootte van de container in. De standaardkleur van inkt is zwart, dus in de voorbeelden in dit artikel wordt een witte achtergrond gebruikt.

Verschillende van deze typen hebben dezelfde namen als typen in de Windows Runtime-naamruimte Windows.UI.Input.Inking, zoals InkPresenter, InkStrokeContainer, en InkHighContrastAdjustment. De inktbedieningselementen gebruiken de Microsoft.UI.Xaml.Controls versies. Stroken en tekenkenmerken maken nog steeds gebruik van Windows Runtime typen, zoals InkStroke, InkDrawingAttributesen InkStrokeBuilder. Als u zowel naamruimten importeert als een dubbelzinnige naam gebruikt, rapporteert de C#-compiler fout CS0104.

In de voorbeelden in dit artikel wordt Microsoft.UI.Xaml.Controls geïmporteerd en worden aliasrichtlijnen gebruikt voor de Windows.UI.Input.Inking-typen die ze nodig hebben:

using Microsoft.UI.Xaml.Controls;
using InkDrawingAttributes = Windows.UI.Input.Inking.InkDrawingAttributes;
using InkStroke = Windows.UI.Input.Inking.InkStroke;

U kunt zowel naamruimten importeren als volledig gekwalificeerde namen gebruiken voor de dubbelzinnige typen, zoals in het WinUI 3 Gallery-voorbeeld.

InkPresenter biedt ook API's op lager niveau die in dit artikel niet worden behandeld:

  • StrokeInput genereert gebeurtenissen wanneer een pennenstreek begint, doorgaat, eindigt of wordt geannuleerd.
  • UnprocessedInput genereert aanwijzergebeurtenissen voor invoer die de presenter niet als inkt verwerkt.
  • InputConfiguration stelt in of het presentatieprogramma invoer verwerkt van de gum van een pen en de primaire barrelknop.
  • InputProcessingConfiguration stelt in of de invoer inkt tekent, inkt wist of niet wordt verwerkt en hoe de presentator invoer via slepen met de rechtermuisknop verwerkt.
  • ActivateCustomDrying geeft een InkSynchronizer terug voor apps die doorgevoerde inkt zelf weergeven.

Stel uw project in

De inkt-API's zijn gemarkeerd als experimenteel. Wanneer u deze gebruikt in een C#-project, rapporteert de C#-compiler waarschuwing CS8305 en de XAML-compiler waarschuwingen WMC1501. Als in uw project waarschuwingen als fouten worden behandeld, stoppen deze waarschuwingen de build.

Nadat u hebt besloten om de experimentele API's te gebruiken, kunt u deze waarschuwingen onderdrukken. Voeg de volgende eigenschappen toe aan het C#-projectbestand (.csproj):

<PropertyGroup>
  <NoWarn>$(NoWarn);CS8305</NoWarn>
  <MSBuildWarningsAsMessages>$(MSBuildWarningsAsMessages);WMC1501</MSBuildWarningsAsMessages>
</PropertyGroup>

In de voorbeelden in dit artikel wordt C# gebruikt. Elk volledig voorbeeld toont de XAML voor een pagina en de code-behind-klasse van de pagina, MainPage.

Inktgebied maken

Pictogram WinUI 3-galerie De WinUI 3 Gallery-app bevat interactieve voorbeelden van WinUI-besturingselementen en -functies. Haal de app op uit de Microsoft Store of blader door de broncode op GitHub.

In het volgende voorbeeld wordt een canvas in een witte Border geplaatst. De code-behind stelt de invoerapparaattypen in, zodat gebruikers kunnen tekenen met een pen, muis of touchinvoer.

<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;
    }
}

Om ze kort te houden, wordt deze code in enkele latere voorbeelden niet herhaald. Stel InputDeviceTypes in op elke pagina waar gebruikers met een muis tekenen.

Een inktwerkbalk koppelen

Plaats een InkToolbar in de buurt van het canvas en stel de eigenschap TargetInkCanvas ervan in om de twee te 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>

Vanaf Windows App SDK 2.5.4-experimenteel kunt u in plaats daarvan een werkbalk rechtstreeks verbinden met een InkPresenter door de TargetInkPresenter eigenschap in te stellen. Stel dit in code in:

inkToolbar.TargetInkPresenter = inkCanvas.InkPresenter;

U kunt deze ook instellen in XAML: TargetInkPresenter="{x:Bind inkCanvas.InkPresenter}". Stel slechts één van de twee doeleigenschappen in.

Op de werkbalk worden standaard knoppen voor balpen, potlood, markeerstift, gum en stencil (liniaal) weergegeven. Zie De inktwerkbalk aanpassen als u wilt wijzigen welke knoppen worden weergegeven.

Invoerapparaten kiezen

De InputDeviceTypes eigenschap maakt gebruik van de CoreInputDeviceTypes opsomming vlaggen uit de Windows.UI.Core naamruimte. Met muisinvoer wordt standaard geen inkt getekend. Waarden combineren om te kiezen welke apparaten inkt gebruiken:

// 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);

Als u wilt stoppen met het verzamelen van inkt vanaf elk apparaat, bijvoorbeeld om een tekening weer te geven die gebruikers niet kunnen wijzigen, stelt u IsInputEnabled in op false:

inkCanvas.InkPresenter.IsInputEnabled = false;

Als u wilt dat gebruikers een invoerapparaat in- of uitschakelen vanaf de werkbalk, raadpleegt u Een aangepaste wisselknop toevoegen.

Tekenkenmerken instellen

Tekenkenmerken, zoals kleur, grootte en vorm van pentip, bepalen hoe streken eruitzien. Ze gebruiken de Windows Runtime InkDrawingAttributes klasse. Als u de kenmerken voor nieuwe streken wilt wijzigen, roept u CopyDefaultDrawingAttributes aan, wijzigt u de kopie en geeft u deze door aan UpdateDefaultDrawingAttributes:

InkDrawingAttributes attributes = inkCanvas.InkPresenter.CopyDefaultDrawingAttributes();
attributes.Color = Colors.DarkBlue;
attributes.Size = new Size(4, 4);
inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(attributes);

Colors is in de Microsoft.UI naamruimte, en Size bevindt zich in de Windows.Foundation naamruimte. De nieuwe attributen zijn van toepassing op pennenstreken die gebruikers na de oproep tekenen. Bestaande lijnen behouden hun attributen.

Wanneer u een InkToolbar aansluit, bepalen het actieve hulpprogramma en de kleur- en grootte-instellingen de tekenkenmerken. Als u de huidige kenmerken wilt lezen, gebruikt u de eigenschap van de werkbalk InkDrawingAttributes.

Penvoorinstellingen maken

Als u geen werkbalk gebruikt, kunt u uw eigen penvoorinstellingen aanbieden. In het volgende voorbeeld worden ballpoint-, potlood-, markeerstift- of kalligrafiekenmerken toegepast wanneer de gebruiker een vooraf ingestelde waarde selecteert of de grootte wijzigt.

<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);
    }
}

Vooraf ingestelde opties voor pen voor balpen, Potlood, Markeerstift en Kalligrafie, met Kalligrafie geselecteerd, boven een schuifregelaar Grootte en een wit canvas. Het canvas toont lusstreken in zwarte balpeninkt, grijze potlood inkt, gele markeerstift inkt en donkerblauwe kalligrafie-inkt.

  • InkDrawingAttributes.CreateForPencil geeft kenmerken voor potloodstreken terug. De Kind eigenschap is alleen-lezen, dus gebruik deze methode om potloodattributen te maken.
  • DrawAsHighlighter geeft de lijn weer als een markeerlaag.
  • PenTip stelt de vorm van de pentip in en PenTipTransform draait of schaalt deze. Een gedraaide rechthoekige tip produceert kalligrafische streken.

Reageren op inktwijzigingen

De presentator activeert StrokesCollected nadat een gebruiker pennenstreken tekent en StrokesErased nadat een gebruiker deze heeft gewist. De Strokes eigenschap van de argumenten van elke gebeurtenis bevat de betrokken streken. Met de volgende code blijft het aantal pennenstreken up-to-date in een TextBlock met de naam 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";
}

Deze gebeurtenissen treden op wanneer gebruikers met inkt tekenen of wissen. Wanneer u strokes in code wijzigt, bijvoorbeeld met Clear, LoadAsync of AddStroke, werkt u uw gebruikersinterface bij na de aanroep.

Inkt opslaan en laden

InkStrokeContainer slaat inkt op in Ink Serialized Format (ISF). Als u wilt kiezen hoe de gegevens worden opgeslagen, geeft u een InkPersistenceFormat waarde door aan SaveAsync:

Format Uitvoer
GifWithEmbeddedIsf Een GIF-afbeelding die de inktgegevens bevat. Afbeeldingsviewers kunnen het bestand weergeven en uw app kan de digitale inkt opnieuw laden om te bewerken.
Isf Alleen inktgegevens, zonder een afbeelding. Dit is het meest compacte formaat.

Voeg de volgende methoden toe aan de code-behind van een pagina die een InkCanvas met de naam inkCanvas bevat. SaveInkAsync schrijft de inkt naar een bestand en LoadInkAsync vervangt de inkt op het canvas door de inkt uit een bestand. LoadAsync verwijdert de bestaande pennenstreken voordat de geladen pennenstreken worden toegevoegd, dus u hoeft Clear niet eerst aan te roepen.

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 en AsInputStream converteren .NET streams naar de Windows Runtime stroomtypen die SaveAsync en LoadAsync gebruiken. Om deze Windows Runtime-methoden te kunnen awaiten, heeft het bestand de using System;-richtlijn nodig. Zonder dit meldt de compiler fout CS4036.

Als u gebruikers een bestand wilt laten kiezen, gebruikt u de Windows App SDK bestandskiezers, die het pad van het geselecteerde bestand retourneren. Zie Een bestand opslaan met een Windows App SDK picker en Bestanden en mappen openen met Windows App SDK pickers voor meer informatie.

Als u inkt in het geheugen wilt bewaren, bijvoorbeeld om deze later in dezelfde sessie te herstellen, slaat u deze op in een InMemoryRandomAccessStream. Geef het resultaat van de stream van GetOutputStreamAt(0) door aan SaveAsync, en geef het resultaat ervan van GetInputStreamAt(0) door aan LoadAsync. In het WinUI 3 Gallery-voorbeeld wordt deze methode gebruikt.

Kopieer en plak inkt

InkStrokeContainer kan pennenstreken naar het klembord kopiëren en er inkt uit invoegen. CopySelectedToClipboard kopieert alleen geselecteerde pennenstreken, dus in het volgende voorbeeld wordt elke pennenstreek op het linkercanvas geselecteerd voordat ze worden gekopieerd. CanPasteFromClipboard controleert of het klembord inkt bevat. PasteFromClipboard voegt de inkt toe op de positie die u doorgeeft en retourneert de begrenzingsrechthoek van het gebied dat is gewijzigd.

<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.";
    }
}

Kopieer het linkercanvas en plak dit in de knoppen voor het rechtercanvas boven twee witte canvassen met dezelfde lus-, spiraal- en golvende lijnen. De tekst hieronder luidt: Geplakte inkt in een gebied van 255 x 69.

Als u met een aantal streken wilt werken, stelt u de Selected-eigenschap in van elke gewenste streek, of roept u SelectWithLine of SelectWithPolyLine aan. Als u de geselecteerde pennenstreken wilt verwijderen of verplaatsen, gebruikt u DeleteSelected of MoveSelected.

Lijnen maken in code

Gebruik de Windows Runtime-klasse InkStrokeBuilder om pennenstreken te maken op basis van punten en voeg ze toe aan het canvas met AddStroke. In het volgende voorbeeld worden een ster en een handtekeningregel toegevoegd.

<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);
    }
}

Voeg ster toe, voeg handtekeninglijn toe en voeg de knoppen Wissen toe boven een wit canvas met een gouden vijfpuntige ster en een grijze handtekeninglijn met een X aan de linkerkant.

  • CreateStrokeFromInkPoints maakt een pennenstreek op basis van InkPoint waarden. Elk punt combineert een positie op het canvas met een drukwaarde.
  • In de Windows Runtime-documentatie wordt CreateStroke aanbevolen in plaats van CreateStrokeFromInkPoints voor pennenstreken die u aan een inktpresentator toevoegt.
  • Wanneer FitToCurve is false, gebruikt de contour rechte segmenten tussen punten in plaats van curven.

Een lijn kan slechts tot één container behoren. Als u pennenstreken naar een ander canvas wilt kopiëren, voegt u klonen van de pennenstreken toe:

private void CopyStrokesTo(InkCanvas target)
{
    foreach (InkStroke stroke in inkCanvas.InkPresenter.StrokeContainer.GetStrokes())
    {
        target.InkPresenter.StrokeContainer.AddStroke(stroke.Clone());
    }
}

De inktwerkbalk aanpassen

Kies de oorspronkelijke bedieningselementen

De InitialControls eigenschap bepaalt welke ingebouwde knoppen door de werkbalk worden aangemaakt:

Value Ingebouwde knoppen
All (standaard) Balpen, potlood, markeerstift, gum en sjabloon (liniaal)
PensOnly Balpen, potlood en markeerstift
AllExceptPens Gummetje en sjabloon (liniaal)
None None. Definieer de gewenste knoppen als onderliggende elementen van de werkbalk.

Vier inktwerkbalken, elk boven een canvas met voorbeeldstreken. Op de werkbalk met het label InitialControls All (standaard) worden de knoppen balpen, potlood, markeerstift, gum en liniaal weergegeven. PensOnly toont de drie penknoppen. AllExceptPens toont de gum- en liniaalknoppen. None plus a pencil toont alleen een potloodknop en het canvas toont grijze potloodstreken.

In het volgende voorbeeld worden dezelfde vier knoppen als in het WinUI 3 Gallery-voorbeeld gedeclareerd, krijgt elke knop een toegankelijke naam en wordt de balpen geselecteerd wanneer de werkbalk wordt geladen:

<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;
            }
        };
    }
}

Als u een opdracht wilt weergeven waarmee alle inkt wordt gewist, stelt u de IsClearAllVisible-eigenschap van de gumknop in op true. De werkbalk genereert de EraseAllClicked gebeurtenis wanneer de gebruiker de opdracht selecteert.

De kleuren en lijndiktes van een pen aanpassen

Elke penknop heeft een Palette selectie penselen en lijnbreedte-instellingen. Stel Palette in de code in. Zie Bekende beperkingenvoor meer informatie. In het volgende voorbeeld wordt het palet van een balpen vervangen door vier merkkleuren, de breedte van de lijn beperkt en worden de kenmerken van de actieve pen weergegeven.

<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})";
        }
    }
}

Een inktwerkbalk met balpen, gum en liniaalknoppen boven de opties Blauw, Groen, Oranje en Paarse penkleur. Tekst leest Kleur #FF0078D4, breedte 6 (bereik 2-16) en het canvas toont lusstreken in blauw, groen, oranje en paars.

SelectedBrushIndex selecteert een kleur uit Palette. SelectedStrokeWidth, MinStrokeWidthen MaxStrokeWidth stel de huidige lijnbreedte en het bereik in waaruit gebruikers kunnen kiezen. De werkbalk activeert InkDrawingAttributesChanged wanneer de kenmerken van het actieve hulpprogramma worden gewijzigd.

Selecteer het actieve gereedschap

Stel ActiveTool in om een tool te selecteren en gebruik ActiveToolChanged om te reageren wanneer de gebruiker een andere tool selecteert. GetToolButton retourneert een ingebouwde werkbalkknop van het soort dat u doorgeeft. De ingebouwde knoppen bestaan mogelijk niet totdat de werkbalk wordt geladen. Selecteer daarom een startgereedschap in de gebeurtenis-handler van de werkbalk Loaded.

<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}";
    }
}

Een inktwerkbalk met de potloodknop geselecteerd. Tekst eronder leest Potlood: Potlood inkt, kleur #FF000000, grootte 4 en het canvas toont grijze potloodstreken.

De afdrukstand wijzigen

Als u de werkbalk verticaal wilt weergeven, bijvoorbeeld naast het canvas, stelt u Orientation in op 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>

Een verticale inktwerkbalk met balpen, potlood, markeerstift, gum en liniaal links van een canvas met zwarte pennenstreken.

Met ButtonFlyoutPlacement de eigenschap wordt ingesteld waar flyouts van knoppen worden geopend ten opzichte van de werkbalk. Met de standaardwaarde Autokan de werkbalk de positie kiezen.

Een aangepaste wisselknop toevoegen

Gebruik een InkToolbarCustomToggleButton om een instelling toe te voegen die gebruikers in- en uitschakelen vanaf de werkbalk. In het volgende voorbeeld wordt een wisselknop toegevoegd waarmee aanraakinvoer wordt bestuurd. Met pen- en muisinvoer wordt altijd inkt getekend.

<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}";
    }
}

Een inktwerkbalk met balpen, potlood, markeerstift, gum en liniaal, gevolgd door een aangepaste schakelknop met een aanraakpictogram, dat is ingeschakeld. De tekst luidt InputDeviceTypes = Touch, Pen, Muis en het canvas toont zwarte pennenstreken.

De wisselknop toont alleen een pictogram, dus geeft het voorbeeld deze een toegankelijke naam en knopinfo. In dit voorbeeld maakt de werkbalk ook de standaardknoppen en wordt de aangepaste wisselknop erna weergegeven.

De werkbalk ondersteunt andere aangepaste knoppen:

De werkbalk in code maken

U kunt ook het canvas en de werkbalk in code maken. In het volgende voorbeeld worden ze toegevoegd aan een Border met de naam inkHost, wordt de werkbalk verbonden met TargetInkPresenter en wordt het potlood geselecteerd wanneer de werkbalk wordt geladen.

<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;
    }
}

Een inktwerkbalk met de geselecteerde potloodknop boven een canvas met grijze potloodstreken.

Bekende beperkingen

  • De inktbesturingselementen zijn alleen beschikbaar in experimentele releases van de Windows App SDK. Ze zijn niet beschikbaar in het stabiele kanaal.
  • De inkt-API's bevatten geen handschrift- of vormherkenning.
  • De muisinvoer tekent pas inkt totdat u CoreInputDeviceTypes.Mouse aan InputDeviceTypes toevoegt.

De volgende problemen zijn gerapporteerd met Windows App SDK experimenteel 2.4.1. In de releaseopmerkingen voor 2.5.4-experimental worden ze niet vermeld als opgelost:

  • De liniaal- en gradenboogstencils werken niet.
  • RenderTargetBitmap legt geen inkt vast. Als u een afbeelding van de inkt wilt maken, slaat u deze op in de GifWithEmbeddedIsf indeling.
  • Als u in XAML de eigenschap Palette van een penknop instelt, wordt er een XAML-parseringsfout veroorzaakt. Stel Palette in plaats daarvan in de code in, zoals wordt weergegeven in De kleuren en breedte van een pen aanpassen.
  • Flyouts van knoppen kunnen leeg lijken en het wijzigen van ButtonFlyoutPlacement wijzigt mogelijk niet waar ze worden geopend.

Wijzigingen in 2.4.1-experimenteel

Als u de inkt-API's in Windows App SDK 2.4.1-experimenteel hebt gebruikt, werkt u de code voor deze wijzigingen bij in 2.5.4-experimenteel:

API in 2.4.1-experimenteel Wijziging in 2.5.4-experimenteel
InkToolbar.TargetInkPresenter (type Object) Het type van de eigenschap is InkPresenter.
methoden voor InkPresenter.GetHighContrastAdjustment en InkPresenter.SetHighContrastAdjustment Vervangen door de InkPresenter.HighContrastAdjustment eigenschap.
methoden voor InkStrokeInput.GetInkPresenter en InkUnprocessedInput.GetInkPresenter Vervangen door een InkPresenter eigenschap in elke klasse.
InkToolbarEraserButton.SelectedEraser, IsStrokeEraserVisible en InkToolbar.EraserFlyoutItemClicked eigenschappen; InkToolbarEraserKind gebeurtenis; InkToolbarEraserFlyoutItemKind, InkToolbarEraserFlyoutItemClickedEventArgs en ArePrecisionErasersVisible typen Verwijderd.
Niet beschikbaar InkPresenter.ActivateCustomDrying methode en InkSynchronizer klasse toegevoegd.

Toegankelijkheid en hoog contrast

Inkt is visuele inhoud, dus plan hoe alle gebruikers uw inktfuncties kunnen gebruiken:

  • Geef elk canvas en elke werkbalk een toegankelijke naam met AutomationProperties.Name, zoals de voorbeelden in dit artikel doen. Geef aangepaste knoppen die alleen een pictogram weergeven een naam en knopinfo.
  • InkCanvasAutomationPeer stelt het canvas beschikbaar aan UI Automation, maar beschrijft de pennenstreken niet. Als de inkt betekenis heeft, zoals een handtekening of een gelabeld diagram, geef een alternatieve tekst op.
  • Tekenen met inkt vereist aanwijzerinvoer. Gebruik inkt niet als de enige manier om een taak te voltooien. Laat gebruikers bijvoorbeeld hun naam typen als alternatief voor het ondertekenen ervan.
  • De automation peers voor de ingebouwde toolknoppen ondersteunen de SelectionItem- en ExpandCollapse-besturingspatronen. Test toetsenbordnavigatie en de fly-outs van de knoppen met de werkbalkconfiguratie die uw app gebruikt.

Wanneer een contrastthema actief is, kan de presentator inktkleuren vervangen door systeemkleuren. Als u dit gedrag wilt kiezen, stelt u de HighContrastAdjustment eigenschap in op een InkHighContrastAdjustment waarde uit de Microsoft.UI.Xaml.Controls naamruimte. In de Windows Runtime documentatie voor de opsomming met dezelfde naam worden de waarden op deze manier beschreven:

Value Inktkleur in een contrastthema
UseSystemColorsWhenNecessary De eigen kleur van de inkt als deze voldoende contrasteert met de achtergrond. Anders een systeemkleur.
UseSystemColors Een systeemkleur. Markeerinkt maakt gebruik van de systeemmarkeringskleur.
UseOriginalColors De kleur van de inkt.
inkCanvas.InkPresenter.HighContrastAdjustment = InkHighContrastAdjustment.UseSystemColorsWhenNecessary;

Om ze kort te houden, stellen de voorbeelden in dit artikel rechtstreeks een witte achtergrond in. Definieer in uw app de achtergrond van het canvas in themawoordenboeken, zodat deze het actieve thema volgt, zoals beschreven in contrastthema's. Test vervolgens uw app met elk contrastthema en controleer of de voorgrond, de achtergrond en de rand onderscheidbaar blijven.

Zie Basisinformatie over toegankelijkheid beschikbaar maken en Toegankelijkheid via het toetsenbord voor meer informatie.

Zie ook