Ovládací prvky inkování pro aplikace WinUI

Important

InkCanvas InkPresenter a InkToolbarjsou experimentální rozhraní API. Podpora rukopisu/inkoustu byla přidána v Windows App SDK 2.4.1-experimental. Tento článek popisuje Windows App SDK 2.5.4-experimentální, který změnil způsob připojení InkToolbar zařízení. Tato rozhraní API nejsou dostupná ve stabilním kanálu. V budoucím vydání se mohou změnit nebo být odebrány. Nepoužívejte je v produkčních aplikacích.

Pomocí experimentálního InkCanvas ovládacího prvku můžete uživatelům umožnit kreslit inkoustem perem, myší nebo dotykem. Přidejte InkToolbar a umožněte uživatelům používat kuličkové pero, tužku, zvýrazňovač a gumu s možnostmi volby barvy a velikosti. Každé plátno má ovládací prvek InkPresenter, který shromažďuje a vykresluje vstup digitálního inkoustu. InkStrokeContainer prezentéra ukládá tahy, takže můžete uložit, načíst, zkopírovat a vytvořit inkoust v kódu.

Pokud chcete vyzkoušet ovládací prvky, nainstalujte balíček Windows App SDK 2.5.4-experimental a odpovídající modul runtime. Odkazy ke stažení a informace o nastavení najdete v tématu Nejnovější soubory ke stažení sady Windows App SDK.

Je to správná kontrola?

Plátno pro rukopis použijte, když uživatelé potřebují:

  • Pořizujte ručně psané poznámky nebo přidávejte poznámky k obsahu
  • Skicujte, kreslete diagramy nebo se podepište.
  • Vytvořte obsah od ruky, který aplikace ukládá a umožňuje jej později upravit.

Experimentální rozhraní API pro digitální inkoust nezahrnují rozpoznávání rukopisu ani rozpoznávání tvarů. Chcete-li zadat text, použijte textové pole.

Pokud potřebujete inkoustový vstup v produkční aplikaci, podívejte se na Známé nedostatky ovládacích prvků, kde najdete informace o stavu těchto ovládacích prvků ve stabilním kanálu a o alternativách.

Vysvětlení modelu digitálního inkoustu

Rozhraní API pro digitální inkoust spolupracují:

API Role
InkCanvas Element XAML, který zobrazuje inkoust a přijímá inkoustový vstup. Jeho InkPresenter vlastnost vrátí prezentér plátna.
InkPresenter Shromažďuje rukopisný vstup, vykresluje tahy a uchovává vstupní nastavení a výchozí atributy kreslení. Nemůžete si InkPresenter vytvořit sami. Získejte to od InkCanvas.InkPresenter.
InkStrokeContainer Ukládá tahy. Slouží k ukládání, načítání, výběru, kopírování a vkládání inkoustu. Získejte to od InkPresenter.StrokeContainer.
InkToolbar Volitelný panel nástrojů, který umožňuje uživatelům zvolit pero, tužku, zvýrazňovač nebo gumu a změnit barvu a velikost inkoustu.

InkCanvas nemá vlastnost Background. Pokud chcete oblasti výkresu dát pozadí a ohraničení, umístěte plátno do kontejneru, například do prvku Border, a nastavte velikost kontejneru. Výchozí barva inkoustu je černá, takže příklady v tomto článku používají bílé pozadí.

Několik z těchto typů má stejné názvy jako typy v oboru názvů prostředí Windows Runtime Windows.UI.Input.Inking, například InkPresenter, InkStrokeContainer a InkHighContrastAdjustment. Ovládací prvky psaní rukou používají verze Microsoft.UI.Xaml.Controls. Tahy a atributy kreslení stále používají prostředí Windows Runtime typy, například InkStroke, InkDrawingAttributesa InkStrokeBuilder. Pokud importujete oba obory názvů a použijete nejednoznačný název, kompilátor C# hlásí chybu CS0104.

Příklady v tomto článku importují Microsoft.UI.Xaml.Controls a používají direktivy aliasů Windows.UI.Input.Inking pro typy, které potřebují:

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

Můžete také importovat obory názvů a použít plně kvalifikované názvy nejednoznačných typů, jak to dělá ukázka galerie WinUI 3.

InkPresenter poskytuje také rozhraní API nižší úrovně, která tento článek nepopisuje:

  • StrokeInput vyvolává události, když tah začne, pokračuje, skončí nebo je zrušen.
  • UnprocessedInput vyvolá události ukazatele pro vstup, který prezentační modul nezpracuje jako digitální inkoust.
  • InputConfiguration nastaví, zda prezentující zpracovává vstup z gumy pera a primárního bočního tlačítka pera.
  • InputProcessingConfiguration nastaví, jestli vstup kreslí digitální inkoust, vymaže digitální inkoust nebo se nezpracuje a jak prezentující zpracovává vstup pravým tlačítkem myši.
  • ActivateCustomDrying vrátí InkSynchronizer pro aplikace, které samy vykreslují potvrzený inkoust.

Nastavení projektu

Rozhraní API pro digitální inkoust jsou označena jako experimentální. Když je použijete v projektu C#, kompilátor C# hlásí upozornění CS8305 a kompilátor XAML hlásí upozornění WMC1501. Pokud váš projekt považuje upozornění za chyby, tato upozornění sestavení zastaví.

Jakmile se rozhodnete použít experimentální rozhraní API, můžete tato upozornění potlačit. Do souboru projektu C# (.csproj) přidejte následující vlastnosti:

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

Příklady v tomto článku používají jazyk C#. Každý úplný příklad ukazuje XAML pro stránku a třídu kódu na pozadí stránky, MainPage.

Vytvořit inkoustové plátno

Ikona galerie WinUI 3 Aplikace Galerie WinUI 3 obsahuje interaktivní příklady ovládacích prvků a funkcí WinUI. Získejte aplikaci z Microsoft Store nebo vyhledejte zdrojový kód na GitHub.

Následující příklad umístí plátno do bílého kontejneru Border. Kód nastaví typy vstupních zařízení tak, aby uživatelé mohli kreslit perem, myší nebo dotykem.

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

Aby byly krátké, některé pozdější příklady tento kód neopakují. Nastaví se InputDeviceTypes na každé stránce, kde uživatelé kreslí myší.

Připojte panel nástrojů rukopisu

Umístěte InkToolbar blízko plátna a nastavte jeho TargetInkCanvas vlastnost pro připojení těchto dvou:

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

Počínaje Windows App SDK 2.5.4-experimental můžete místo toho připojit panel nástrojů přímo k InkPresenter nastavením vlastnosti TargetInkPresenter. Nastavte ho v kódu:

inkToolbar.TargetInkPresenter = inkCanvas.InkPresenter;

Můžete ho také nastavit v jazyce XAML: TargetInkPresenter="{x:Bind inkCanvas.InkPresenter}". Nastavte pouze jednu ze dvou cílových vlastností.

Ve výchozím nastavení panel nástrojů zobrazuje tlačítka kuličkového pera, tužky, zvýrazňovače, gumy a šablony (pravítka). Pokud chcete změnit zobrazení tlačítek, přečtěte si téma Přizpůsobení panelu nástrojů rukopisu.

Vyberte vstupní zařízení

Vlastnost InputDeviceTypes používá CoreInputDeviceTypes výčty příznaků z Windows.UI.Core oboru názvů. Vstup myši ve výchozím nastavení nekreslí inkoust. Zkombinujte hodnoty a vyberte, která zařízení vykreslují rukopisné stopy:

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

Pokud chcete zastavit shromažďování inkoustu ze všech zařízení, například když chcete zobrazit výkres, který uživatelé nemůžou změnit, nastavte IsInputEnabled na false:

inkCanvas.InkPresenter.IsInputEnabled = false;

Pokud chcete uživatelům povolit zapnutí nebo vypnutí vstupního zařízení z panelu nástrojů, přečtěte si článek Přidání vlastního přepínacího tlačítka.

Nastavit atributy výkresu

Atributy kreslení, jako je barva, velikost a tvar hrotu pera, určují vzhled tahů. Používají třídu prostředí Windows RuntimeInkDrawingAttributes. Pokud chcete změnit atributy pro nové tahy, zavolejte CopyDefaultDrawingAttributes, změňte kopii a předejte ji do UpdateDefaultDrawingAttributes:

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

Colors je v Microsoft.UI oboru názvů a Size je v Windows.Foundation oboru názvů. Nové atributy platí pro tahy, které uživatelé nakreslí po hovoru. Existující tahy udržují své atributy.

Když připojíte InkToolbar, aktivní nástroj a jeho nastavení barvy a velikosti určují atributy kreslení. Ke čtení aktuálních atributů použijte vlastnost InkDrawingAttributes panelu nástrojů.

Vytvořit předvolby pera

Pokud nepoužíváte panel nástrojů, můžete nabídnout vlastní předvolby pera. Následující příklad použije atributy kuličkového pera, tužky, zvýrazňovače nebo kaligrafie, když uživatel vybere předvolbu nebo změní velikost.

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

Přednastavené možnosti pera pro Ballpoint, Pencil, Highlighter a Calligraphy, s vybranou možností Calligraphy nad posuvníkem Velikost a bílým plátnem. Na plátně se zobrazují tahy s černým kuličkovým inkoustem, šedým inkoustem tužky, žlutým inkoustem zvýrazňovače a tmavě modrou barvou rukopisu.

  • InkDrawingAttributes.CreateForPencil vrátí atributy pro tahy tužky. Vlastnost Kind je jen pro čtení, takže tuto metodu použijte k vytvoření atributů nástroje tužka.
  • DrawAsHighlighter vykreslí tah jako překryv zvýrazňovače.
  • PenTip nastaví tvar hrotu pera a PenTipTransform ho otočí nebo změní jeho měřítko. Otočený obdélníkový hrot vytváří tahy ve stylu kaligrafie.

Reagovat na změny rukopisného vstupu

Prezentující vyvolá StrokesCollected poté, co uživatel nakreslí tahy, a StrokesErased poté, co je uživatel smaže. Vlastnost Strokes argumentů každé události obsahuje ovlivněné tahy. Následující kód udržuje počet tahů aktuální v objektu TextBlock s názvem 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";
}

K těmto událostem dochází, když uživatelé kreslí nebo vymažou inkoust. Když změníte tahy v kódu, například pomocí Clear, LoadAsync nebo AddStroke, aktualizujte uživatelské rozhraní po volání.

Uložení a načtení inkoustu

InkStrokeContainer uloží digitální inkoust ve formátu ISF (Ink Serialized Format). Pokud chcete zvolit způsob ukládání dat, předejte hodnotu InkPersistenceFormat parametru SaveAsync:

Format Output
GifWithEmbeddedIsf Obrázek GIF, který obsahuje data rukopisu. Prohlížeče obrázků můžou soubor zobrazit a vaše aplikace může inkoustová data znovu načíst pro úpravy.
Isf Pouze data inkoustu bez obrázku Toto je nejkompaktnější formát.

Přidejte následující metody do code-behind stránky, která obsahuje prvek InkCanvas s názvem inkCanvas. SaveInkAsync zapíše inkoust do souboru a LoadInkAsync nahradí inkoust na plátně inkoustem ze souboru. LoadAsync odebere existující tahy před tím, než přidá načtené tahy, takže nemusíte volat Clear jako první.

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 a AsInputStream převádějí datové proudy .NET na typy datových proudů prostředí Windows Runtime, které používají SaveAsync a LoadAsync. Pokud chcete u těchto metod prostředí Windows Runtime použít await, soubor potřebuje direktivu using System;. Bez něj kompilátor hlásí chybu CS4036.

Pokud chcete uživatelům umožnit výběr souboru, použijte dialogy pro výběr souborů ve Windows App SDK, které vrátí cestu k vybranému souboru. Další informace najdete v tématu Uložení souboru pomocí nástroje pro výběr ve Windows App SDK a otevření souborů a složek pomocí nástrojů pro výběr ve Windows App SDK.

Pokud chcete zachovat rukopis v paměti, například ho obnovit později ve stejné relaci, uložte ho do InMemoryRandomAccessStream. Předejte výsledek streamuGetOutputStreamAt(0) do GetInputStreamAt(0) a jeho LoadAsync výsledek předejte do SaveAsync. Tento přístup používá ukázka galerie WinUI 3.

Kopírování a vkládání inkoustu

Pomocí InkStrokeContainer lze zkopírovat tahy do schránky a vložit z ní inkoust. CopySelectedToClipboard zkopíruje jenom vybrané tahy, takže následující příklad vybere každý tah na levém plátně předtím, než je zkopíruje. CanPasteFromClipboard zkontroluje, zda schránka obsahuje inkoust. PasteFromClipboard přidá inkoustové tahy na pozici, kterou předáte, a vrátí ohraničující obdélník oblasti, která se změnila.

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

Tlačítka Kopírovat levé plátno a Vložit do pravého plátna jsou nad dvěma bílými plátny, která zobrazují stejné smyčky, spirály a vlnité tahy. Text níže zní: Vložený rukopis do oblasti 255 x 69.

Chcete-li pracovat s některými tahy, nastavte vlastnost Selected každého požadovaného tahu nebo zavolejte SelectWithLine nebo SelectWithPolyLine. Chcete-li odstranit nebo přesunout vybrané tahy, použijte DeleteSelected nebo MoveSelected.

Vytvoření tahů v kódu

Pomocí třídy prostředí Windows Runtime InkStrokeBuilder můžete vytvářet tahy z bodů a přidat je na plátno pomocí AddStroke. Následující příklad přidá hvězdičku a podpisový řádek.

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

Tlačítka Přidat hvězdičku, Přidat řádek podpisu a Vymazat nad bílým plátnem, která zobrazuje zlatou pěticípou hvězdu a šedou čáru podpisu s X na levém konci.

  • CreateStrokeFromInkPoints vytvoří tah z InkPoint hodnot. Každý bod kombinuje pozici na plátně s hodnotou tlaku.
  • Dokumentace prostředí Windows Runtime doporučuje pro tahy, které přidáte do prezentátoru rukopisu, použít CreateStrokeFromInkPoints místo CreateStroke.
  • Pokud FitToCurve je false, tah používá rovné segmenty mezi body místo křivek.

Čára může patřit pouze do jednoho kontejneru. Pokud chcete zkopírovat tahy na jiné plátno, přidejte klony tahů:

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

Přizpůsobte panel nástrojů rukopisu

Vyberte počáteční ovládací prvky

Vlastnost InitialControls určuje, která předdefinovaná tlačítka panel nástrojů vytvoří:

Value Integrovaná tlačítka
All (výchozí) Kuličkové pero, tužka, zvýrazňovač, guma a šablona (pravítko)
PensOnly Kuličkové pero, tužka a zvýrazňovač
AllExceptPens Guma a šablona (pravítko)
None None. Deklarujte požadovaná tlačítka jako podřízené položky panelu nástrojů.

Čtyři panely nástrojů pro rukopis, každý nad plátnem s ukázkovými tahy. Panel nástrojů s popiskem InitialControls All (výchozí) zobrazuje tlačítka kuličkového pera, tužky, zvýrazňovače, gumy a pravítka. PensOnly zobrazuje tři tlačítka pera. AllExceptPens zobrazuje tlačítka gumy a pravítka. None plus a pencil zobrazuje jenom tlačítko tužky a jeho plátno zobrazuje šedé tahy tužky.

Následující příklad deklaruje stejné čtyři tlačítka jako ukázka galerie WinUI 3, každému z nich přiřadí přístupný název a vybere kuličkové pero při načtení panelu nástrojů:

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

Pokud chcete zobrazit příkaz, který vymaže veškerý rukopis, nastavte vlastnost tlačítka gumy IsClearAllVisible na truehodnotu . Panel nástrojů vyvolá událost EraseAllClicked, když uživatel vybere příkaz.

Přizpůsobte barvy a šířky pera

Každé tlačítko pera má Palette štětců a nastavení šířky tahu. Nastavte Palette v kódu. Podrobnosti najdete v tématu Známá omezení. Následující příklad nahrazuje paletu kuličkového pera čtyřmi barvami značky, omezuje šířku tahu a zobrazuje atributy aktivního pera.

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

Panel nástrojů rukopisu s tlačítky kuličkového pera, gumy a pravítka nad možnostmi barev pera: modrá, zelená, oranžová a fialová. Text zní: Barva #FF0078D4, šířka 6 (rozsah 2–16) a plátno zobrazuje smyčkové tahy modře, zeleně, oranžově a fialově.

SelectedBrushIndex vybere barvu z Palette. SelectedStrokeWidth, MinStrokeWidth a MaxStrokeWidth nastavují aktuální šířku tahu a rozsah, ze kterého mohou uživatelé vybírat. Panel nástrojů vyvolá InkDrawingAttributesChanged , když se změní atributy aktivního nástroje.

Vyberte aktivní nástroj

Nastavte ActiveTool tak, aby bylo možné vybrat nástroj, a obslužnou rutinu ActiveToolChanged pro odpověď, když uživatel vybere jiný nástroj. GetToolButton vrátí integrované tlačítko nástroje typu, který předáte. Vestavěná tlačítka nemusí existovat, dokud se panel nástrojů nenačte, takže v obslužné rutině události panelu nástrojů Loaded vyberte výchozí nástroj.

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

Panel nástrojů rukopisu s vybraným tlačítkem tužky Text pod ním čte tužku: Tužka, barva #FF000000, velikost 4 a plátno zobrazuje šedé tahy tužky.

Změnit orientaci

Pokud chcete panel nástrojů zobrazit svisle, například vedle plátna, nastavte Orientation na 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>

Svislý panel nástrojů rukopisu s tlačítky pro kuličkové pero, tužku, zvýrazňovač, gumu a pravítko vlevo od plátna s černými tahy rukopisu.

Vlastnost ButtonFlyoutPlacement nastaví, kde se rozevírají místní nabídky tlačítek vzhledem k panelu nástrojů. Výchozí hodnota Autoumožňuje panelu nástrojů zvolit pozici.

Přidat vlastní přepínací tlačítko

Pomocí InkToolbarCustomToggleButton přidejte nastavení, které uživatelé na panelu nástrojů zapínají a vypínají. Následující příklad přidá přepínač, který řídí dotykové zadávání. Pero a myš vždy kreslí rukopis.

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

Panel nástrojů rukopisu s kuličkovým perem, tužkou, zvýrazňovačem, gumou a tlačítky pravítka následovaným vlastním přepínacím tlačítkem s ikonou dotykového ovládání, která je zapnutá. Text uvádí InputDeviceTypes = Touch, Pen, Mouse, a plátno zobrazuje černé tahy rukopisu.

Přepínač zobrazuje jenom ikonu, takže mu příklad dává přístupný název a popisek. V tomto příkladu panel nástrojů také vytváří výchozí tlačítka a za nimi se zobrazí vlastní přepínač.

Panel nástrojů podporuje další vlastní tlačítka:

Vytvořte panel nástrojů v kódu

Plátno a panel nástrojů můžete vytvořit také v kódu. Následující příklad je přidá do objektu Border s názvem inkHost, připojí panel nástrojů pomocí TargetInkPresenter a vybere tužku při načtení panelu nástrojů.

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

Panel nástrojů rukopisu s tlačítkem tužky vybraným nad plátnem se šedými tahy tužky

Známá omezení

  • Ovládací prvky pro digitální inkoust jsou k dispozici pouze v experimentálních verzích Windows App SDK. Nejsou dostupné ve stabilním kanálu.
  • Rozhraní API pro digitální inkoust nezahrnují rozpoznávání rukopisu ani rozpoznávání tvarů.
  • Vstup myši nekreslí rukopis, dokud nepřidáte CoreInputDeviceTypes.Mouse do InputDeviceTypes.

U Windows App SDK experimentální verze 2.4.1 byly hlášeny následující problémy. V poznámkách k experimentální verzi 2.5.4 nejsou uvedeny jako opravené:

  • Šablony pravítka a úhloměru nefungují.
  • RenderTargetBitmap nezachytí inkoust. Pokud chcete vytvořit obrázek inkoustu, uložte ho ve formátu GifWithEmbeddedIsf.
  • Nastavení vlastnosti Palette tlačítka pera v XAML způsobí chybu analýzy XAML. Nastavte Palette místo toho v kódu, jak je znázorněno v části Přizpůsobení barev a šířek pera.
  • Kontextové rámečky tlačítek se můžou zobrazovat prázdné a změna ButtonFlyoutPlacement nemusí změnit místo, kde se otevřou.

Změny z experimentální verze 2.4.1

Pokud jste použili rozhraní API pro rukopisný vstup v Windows App SDK 2.4.1 experimentální, aktualizujte kód pro tyto změny v experimentální verzi 2.5.4:

Rozhraní API v 2.4.1-experimental Změna v 2.5.4-experimental
InkToolbar.TargetInkPresenter (typ Object) Typ vlastnosti je InkPresenter.
metody InkPresenter.GetHighContrastAdjustment a InkPresenter.SetHighContrastAdjustment Nahrazeno vlastností InkPresenter.HighContrastAdjustment.
metody InkStrokeInput.GetInkPresenter a InkUnprocessedInput.GetInkPresenter Nahrazeno InkPresenter vlastností pro každou třídu.
InkToolbarEraserButton.SelectedEraser, IsStrokeEraserVisible a ArePrecisionErasersVisible vlastnosti; InkToolbarEraserKind událost; InkToolbar.EraserFlyoutItemClicked, InkToolbarEraserFlyoutItemKind a InkToolbarEraserFlyoutItemClickedEventArgs typy Odstraněn.
Není k dispozici InkPresenter.ActivateCustomDrying metoda a InkSynchronizer třída byly přidány.

Přístupnost a vysoký kontrast

Digitální inkoust je vizuální obsah, takže naplánujte, jak můžou všichni uživatelé používat funkce digitálního inkoustu:

  • Dejte každému plátnu a liště nástrojů přístupný název pomocí AutomationProperties.Name, jak je uvedeno v příkladech v tomto článku. Dejte vlastním tlačítkům, která zobrazují jen ikonu, název a popisek.
  • InkCanvasAutomationPeer zpřístupňuje plátno pro model UI Automation, ale nepopisuje tahy. Pokud inkoustový obsah má význam, například podpis nebo označený diagram, zadejte alternativní text.
  • Kreslení rukopisem vyžaduje vstup ukazovacím zařízením. Neudělejte z inkoustu jediný způsob, jak dokončit úkol. Můžete například umožnit uživatelům, aby zadali svoje jméno jako alternativu k podpisu.
  • Protějšky automatizace pro integrovaná tlačítka nástrojů podporují řídicí vzory SelectionItem a ExpandCollapse. Otestujte navigaci pomocí klávesnice a rozbalovací nabídky tlačítek s konfigurací panelu nástrojů, kterou vaše aplikace používá.

Pokud je kontrastní motiv aktivní, může přednášející nahradit barvy inkoustu barvami systému. Chcete-li zvolit toto chování, nastavte HighContrastAdjustment vlastnost na InkHighContrastAdjustment hodnotu z Microsoft.UI.Xaml.Controls oboru názvů. Dokumentace k prostředí Windows Runtime výčtu se stejným názvem popisuje hodnoty tímto způsobem:

Value Barva inkoustu v kontrastním motivu
UseSystemColorsWhenNecessary Vlastní barva inkoustu, pokud je dostatečně kontrastní s pozadím. V opačném případě je to barva systému.
UseSystemColors Barva systému. Inkoust zvýrazňovače používá barvu zvýrazňovače systému.
UseOriginalColors Barva inkoustu.
inkCanvas.InkPresenter.HighContrastAdjustment = InkHighContrastAdjustment.UseSystemColorsWhenNecessary;

Aby byly krátké, v příkladech v tomto článku je bílé pozadí nastaveno přímo. V aplikaci definujte pozadí plátna ve slovnících motivů tak, aby se řídilo aktivním motivem, jak je popsáno v Kontrastních motivech. Pak otestujte aplikaci s každým kontrastním motivem a zkontrolujte, jestli barva popředí, pozadí a ohraničení zůstanou rozlišitelné.

Další informace najdete v tématu Zveřejnění základních informací o přístupnosti a přístupnosti pomocí klávesnice.

Viz také