Kommentar
Åtkomst till den här sidan kräver auktorisering. Du kan prova att logga in eller ändra kataloger.
Åtkomst till den här sidan kräver auktorisering. Du kan prova att ändra kataloger.
Important
InkCanvas, InkToolbaroch InkPresenter är experimentella API:er. Pennanteckningsstöd lades till i Windows App SDK 2.4.1-experimental. Den här artikeln beskriver Windows App SDK 2.5.4-experimental, som ändrade hur du ansluter till en InkToolbar. Dessa API:er är inte tillgängliga i den stabila kanalen. De kan ändras eller tas bort i en framtida version. Använd dem inte i produktionsappar.
Använd den experimentella InkCanvas kontrollen för att låta användarna rita med bläck med penna, mus eller touch. Lägg till en InkToolbar för att ge användarna en kulspetspenna, penna, överstrykningspenna och suddgummi, med färg- och storleksalternativ. Varje arbetsyta har en InkPresenter som samlar in och återger bläckindata.
InkStrokeContainer i presentatören lagrar linjerna så att du kan spara, läsa in, kopiera och skapa pennanteckningar i kod.
Om du vill prova kontrollerna installerar du det experimentella Windows App SDK 2.5.4-paketet och motsvarande körmiljö. För nedladdningslänkar och installationsinformation, se Senaste Windows App SDK-nedladdningar.
Är det här rätt kontroll?
Använd en arbetsyta för penninmatning när användarna behöver:
- Gör handskrivna anteckningar eller annotera innehåll.
- Skissa, rita diagram eller signera ditt namn.
- Skapa frihandsinnehåll som din app sparar och låter användarna redigera senare.
API:erna för experimentell penninmatning innehåller inte handskrift eller formigenkänning. Om du vill samla in text använder du en textruta.
Om du behöver bläckinmatning i en produktionsapp kan du läsa Kända kontrollluckor för status för dessa kontroller i den stabila kanalen och för alternativ.
Förstå inkningsmodellen
API:erna för inkning fungerar tillsammans:
| API | Befattning |
|---|---|
InkCanvas |
XAML-elementet som visar bläck och tar emot bläckindata. Dess InkPresenter egenskap returnerar presenter för ritytan. |
InkPresenter |
Samlar in penninmatning, återger streck och innehåller indatainställningarna och standardritningsattributen. Du kan inte skapa en InkPresenter själv. Hämta den från InkCanvas.InkPresenter. |
InkStrokeContainer |
Lagrar streck. Använd den för att spara, läsa in, välja, kopiera och klistra in bläck. Hämta den från InkPresenter.StrokeContainer. |
InkToolbar |
Ett valfritt verktygsfält där användarna kan välja penna, blyertspenna, överstrykningspenna eller raderare och ändra bläckfärg och storlek på pennan. |
InkCanvas har ingen Background egenskap. Om du vill ge ritningsområdet en bakgrund och en kantlinje placerar du arbetsytan i en behållare, till exempel en Border, och anger storleken på behållaren. Standardbläckfärgen är svart, så exemplen i den här artikeln använder en vit bakgrund.
Flera av dessa typer har samma namn som typer i namnrymden Windows Runtime Windows.UI.Input.Inking, till exempel InkPresenter, InkStrokeContainer och InkHighContrastAdjustment. Pennanteckningskontrollerna använder Microsoft.UI.Xaml.Controls-versionerna. Penseldrag och ritattribut använder fortfarande Windows Runtime-typer, till exempel InkStroke, InkDrawingAttributes och InkStrokeBuilder. Om du importerar båda namnrymderna och använder ett tvetydigt namn rapporterar C#-kompilatorn felet CS0104.
Exemplen i den här artikeln importerar Microsoft.UI.Xaml.Controls och använder aliasdirektiv för de Windows.UI.Input.Inking typer som de behöver:
using Microsoft.UI.Xaml.Controls;
using InkDrawingAttributes = Windows.UI.Input.Inking.InkDrawingAttributes;
using InkStroke = Windows.UI.Input.Inking.InkStroke;
Du kan också importera båda namnrymderna och använda fullständigt kvalificerade namn för de tvetydiga typerna, som WinUI 3-galleriexemplet gör.
InkPresenter innehåller också API:er på lägre nivå som den här artikeln inte omfattar:
-
StrokeInputgenererar händelser när en stroke startar, fortsätter, slutar eller avbryts. -
UnprocessedInputgenererar pekarhändelser för indata som presentatören inte bearbetar som pennanteckning. -
InputConfigurationanger om presentatörenheten bearbetar indata från en pennas suddgummi och primära sidoknapp. -
InputProcessingConfigurationanger om indata ritar pennanteckning, raderar pennanteckningar eller inte bearbetas och hur presentatorkomponenten hanterar högerdrag. -
ActivateCustomDryingreturnerar enInkSynchronizerför appar som själva återger bekräftat bläck.
Konfigurera projektet
API:erna för bläck är markerade som experimentella. När du använder dem i ett C#-projekt rapporterar C#-kompilatorn varning CS8305 och XAML-kompilatorn rapporterar varning WMC1501. Om ditt projekt behandlar varningar som fel stoppar dessa varningar bygget.
När du har bestämt dig för att använda de experimentella API:erna kan du ignorera dessa varningar. Lägg till följande egenskaper i C#-projektfilen (.csproj):
<PropertyGroup>
<NoWarn>$(NoWarn);CS8305</NoWarn>
<MSBuildWarningsAsMessages>$(MSBuildWarningsAsMessages);WMC1501</MSBuildWarningsAsMessages>
</PropertyGroup>
Exemplen i den här artikeln är skrivna i C#. Varje fullständigt exempel visar XAML för en sida och sidans code-behind-klass, MainPage.
Skapa en bläckarbetsyta
![]()
WinUI 3-galleriappen innehåller interaktiva exempel på WinUI-kontroller och funktioner. Hämta appen från Microsoft Store eller bläddra i källkoden på GitHub.
I följande exempel placeras en canvas inuti en vit Border. Code-behind anger enhetstyperna för indata så att användarna kan rita med en penna, mus eller touch.
<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;
}
}
För att hålla dem korta upprepar vissa senare exempel inte den här koden. Ange InputDeviceTypes på varje sida där användare ritar med en mus.
Anslut ett ritverktygsfält
Placera en InkToolbar nära arbetsytan och ställ in dess TargetInkCanvas egenskap för att ansluta de två:
<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>
Från och med Windows App SDK 2.5.4-experimentell kan du i stället ansluta ett verktygsfält direkt till ett InkPresenter genom att ange egenskapen TargetInkPresenter. Ställ in det i kod:
inkToolbar.TargetInkPresenter = inkCanvas.InkPresenter;
Du kan också ange den i XAML: TargetInkPresenter="{x:Bind inkCanvas.InkPresenter}". Ange endast en av de två egenskaperna för målet.
Som standard visar verktygsfältet knapparna kulspetspenna, blyertspenna, överstrykningspenna, radergummi och stencil (linjal). Om du vill ändra vilka knappar som visas, se Anpassa verktygsfältet för pennanteckningar.
Välj indataenheter
Egenskapen InputDeviceTypes använder uppräkning av CoreInputDeviceTypes flaggor från Windows.UI.Core namnområdet. Musinmatning ritar inte bläck som standard. Kombinera värden för att välja vilka enheter som ska rita med bläck:
// 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);
Om du vill sluta samla in digitalt bläck från varje enhet, till exempel för att visa en ritning som användarna inte kan ändra, ställer du in IsInputEnabled på false:
inkCanvas.InkPresenter.IsInputEnabled = false;
Information om hur du låter användare aktivera eller inaktivera en indataenhet från verktygsfältet finns i Lägg till en anpassad växlingsknapp.
Ange ritattribut
Ritattribut, till exempel färg, storlek och pennspetsform, styr hur linjer ser ut. De använder klassen Windows RuntimeInkDrawingAttributes. Om du vill ändra attributen för nya linjer, anropa CopyDefaultDrawingAttributes, ändra kopian och skicka den till UpdateDefaultDrawingAttributes:
InkDrawingAttributes attributes = inkCanvas.InkPresenter.CopyDefaultDrawingAttributes();
attributes.Color = Colors.DarkBlue;
attributes.Size = new Size(4, 4);
inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(attributes);
Colors finns i Microsoft.UI namnområdet och Size finns i Windows.Foundation namnområdet. De nya attributen gäller för streck som användarna ritar efter funktionsanropet. Befintliga linjer behåller sina attribut.
När du ansluter ett InkToolbar avgör det aktiva verktyget och dess färg- och storleksinställningar ritattributen. Om du vill läsa de aktuella attributen använder du verktygsfältets InkDrawingAttributes egenskap.
Skapa pennförinställningar
Om du inte använder ett verktygsfält kan du skapa egna pennförinställningar. I följande exempel används attribut för kulspetspenna, blyertspenna, highlighter eller kalligrafi när användaren väljer en förinställning eller ändrar storleken.
<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.CreateForPencilreturnerar attribut för pennstreck. EgenskapenKindär skrivskyddad, så använd den här metoden för att skapa blyertsattribut. -
DrawAsHighlighterrenderar strecket som ett markeringsöverlägg. -
PenTipbestämmer pennspetsens form ochPenTipTransformroterar eller skalar den. En roterad rektangulär penna ger linjer i kalligrafistil.
Svara på bläckändringar
Presentatören visar StrokesCollected efter att en användare ritat linjer och StrokesErased när en användare har raderat dem. Egenskapen Strokes för varje händelses argument innehåller de berörda pennstrecken. Följande kod håller antalet streck uppdaterat i ett TextBlock namngivet 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";
}
Dessa händelser inträffar när användare ritar eller raderar bläck. När du ändrar streck i koden, till exempel med Clear, LoadAsync eller AddStroke, uppdaterar du användargränssnittet efter anropet.
Spara och läs in bläck
InkStrokeContainer sparar digitalt bläck i Ink Serialized Format (ISF). Om du vill välja hur data lagras skickar du ett InkPersistenceFormat värde till SaveAsync:
| Format | Output |
|---|---|
GifWithEmbeddedIsf |
En GIF-bild som innehåller bläckdata. Bildvisningsprogram kan visa filen och appen kan läsa in pennanteckningen igen för redigering. |
Isf |
Endast bläckdata, utan en bild. Det här är det mest kompakta formatet. |
Lägg till följande metoder i koden bakom på en sida som innehåller en InkCanvas med namnet inkCanvas.
SaveInkAsync skriver pennanteckningen till en fil och LoadInkAsync ersätter pennanteckningen på arbetsytan med pennanteckningen från en fil.
LoadAsync tar bort befintliga streck innan de inlästa strecken läggs till, så du behöver inte anropa Clear först.
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 och AsInputStream konverterar .NET-strömmar till de Windows Runtime-strömtyper som SaveAsync och LoadAsync använder. För att invänta dessa Windows Runtime-metoder behöver filen direktivet using System;. Utan den rapporterar kompilatorn felet CS4036.
Om du vill låta användarna välja en fil använder du Windows App SDK filväljare, som returnerar sökvägen till den valda filen. Mer information finns i Spara en fil med en filväljare i Windows App SDK och Öppna filer och mappar med filväljare i Windows App SDK.
Om du vill behålla bläck i minnet, till exempel för att återställa det senare i samma session, sparar du det i en InMemoryRandomAccessStream. Skicka strömmens GetOutputStreamAt(0)-resultat till SaveAsync och skicka dess GetInputStreamAt(0)-resultat till LoadAsync. WinUI 3-galleriexemplet använder den här metoden.
Kopiera och klistra in bläck
InkStrokeContainer kan kopiera penndrag till Urklipp och klistra in pennanteckningar från det.
CopySelectedToClipboard kopierar endast markerade penseldrag, så i följande exempel markeras varje penseldrag på den vänstra arbetsytan innan de kopieras.
CanPasteFromClipboard kontrollerar om urklipp innehåller bläck.
PasteFromClipboard lägger till bläcket vid den position som du skickar och returnerar avgränsningsrektangeln för området som ändrades.
<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.";
}
}
Om du vill arbeta med några av strecken anger du egenskapen Selected för varje streck som du vill använda, eller anropar SelectWithLine eller SelectWithPolyLine. Om du vill ta bort eller flytta de markerade linjerna använder du DeleteSelected eller MoveSelected.
Skapa streck i kod
Använd klassen Windows Runtime InkStrokeBuilder för att skapa streck från punkter och lägga till dem på arbetsytan med AddStroke. I följande exempel läggs en stjärna och en signaturrad till.
<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);
}
}
-
CreateStrokeFromInkPointsskapar en linje frånInkPointvärden. Varje punkt kombinerar en position på arbetsytan med ett tryckvärde. - Dokumentationen för Windows Runtime rekommenderar
CreateStrokeFromInkPointsi stället förCreateStrokeför pennstreck som du lägger till i en pennanteckningspresentatör. - När
FitToCurveärfalseanvänder stroken raka segment mellan punkter i stället för kurvor.
En kontur kan bara tillhöra en container. Om du vill kopiera streck till en annan arbetsyta lägger du till kloner av strecken:
private void CopyStrokesTo(InkCanvas target)
{
foreach (InkStroke stroke in inkCanvas.InkPresenter.StrokeContainer.GetStrokes())
{
target.InkPresenter.StrokeContainer.AddStroke(stroke.Clone());
}
}
Anpassa verktygsfältet för bläck
Välj de ursprungliga kontrollerna
Egenskapen InitialControls avgör vilka inbyggda knappar som verktygsfältet skapar:
| Value | Inbyggda knappar |
|---|---|
All (standardinställning) |
Kulspetspenna, blyertspenna, överstrykningspenna, radergummi och mall (linjal) |
PensOnly |
Kulspetspenna, blyertspenna och överstrykningspenna |
AllExceptPens |
Radergummi och schablon (linjal) |
None |
None. Deklarera de knappar som du vill ha som underordnade till verktygsfältet. |
I följande exempel deklarerar samma fyra knappar som WinUI 3-galleriexemplet, ger var och en ett tillgängligt namn och väljer kulspetspennan när verktygsfältet läses in:
<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;
}
};
}
}
Om du vill visa ett kommando som raderar alla pennanteckningar anger du egenskapen för suddknappen IsClearAllVisible till true. Verktygsfältet genererar EraseAllClicked händelsen när användaren väljer kommandot.
Anpassa en pennas färger och bredder
Varje pennknapp har en Palette penslar och inställningar för linjebredd. Ange Palette i kod. Mer information finns i Kända begränsningar. I följande exempel ersätts en kulspetspennas palett med fyra varumärkesfärger, linjebredden begränsas och attributen för den aktiva pennan visas.
<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})";
}
}
}
Välj en färg från Palette.
SelectedStrokeWidth, MinStrokeWidthoch MaxStrokeWidth ange den aktuella linjebredden och det intervall som användarna kan välja mellan. Verktygsfältet utlöser InkDrawingAttributesChanged när attributen för det aktiva verktyget ändras.
Välj det aktiva verktyget
Ställ in ActiveTool för att välja ett verktyg och hantera ActiveToolChanged för att svara när användaren väljer ett annat.
GetToolButton returnerar en inbyggd verktygsknapp av den typ som du anger. De inbyggda knapparna kanske inte finns förrän verktygsfältet har lästs in, så välj ett startverktyg i verktygsfältets Loaded händelsehanterare.
<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}";
}
}
Ändra orienteringen
Om du vill visa verktygsfältet lodrätt, till exempel bredvid arbetsytan, ställer du in Orientation på 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>
Egenskapen ButtonFlyoutPlacement anger var knapputfällningar öppnas i förhållande till verktygsfältet. Standardvärdet, Auto, gör att verktygsfältet kan välja position.
Lägg till en anpassningsbar växlare
Använd en InkToolbarCustomToggleButton för att lägga till en inställning som användarna aktiverar och inaktiverar från verktygsfältet. I följande exempel läggs en växlingsknapp till som styr pekindata. Inmatning med penna och mus ritar alltid med bläck.
<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}";
}
}
Växlingsknappen visar bara en ikon, så exemplet ger den ett tillgängligt namn och en knappbeskrivning. I det här exemplet skapar verktygsfältet också sina standardknappar och den anpassade växlingsknappen visas efter dem.
Verktygsfältet stöder andra anpassade knappar:
-
InkToolbarCustomToolButtonLägg till ett eget verktyg. -
InkToolbarCustomPenButtonlägger till ett ritverktyg som skapar egna ritattribut. Ställ in egenskapenCustomPenpå en instans av en klass som härstammar frånInkToolbarCustomPenoch åsidosätter metodenCreateInkDrawingAttributesCore.
Skapa verktygsfältet i kod
Du kan också skapa arbetsytan och verktygsfältet i koden. I följande exempel läggs de till i ett Border med namnet inkHost, verktygsfältet ansluts med TargetInkPresenter och pennan väljs när verktygsfältet läses in.
<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;
}
}
Kända begränsningar
- Pennanteckningskontrollerna är endast tillgängliga i experimentella versioner av Windows App SDK. De är inte tillgängliga i den stabila kanalen.
- API:erna för digitalt bläck omfattar inte handskrifts- eller formigenkänning.
- Musinmatning ritar inte digitalt bläck förrän du lägger till
CoreInputDeviceTypes.MouseiInputDeviceTypes.
Följande problem rapporterades med Windows App SDK 2.4.1-experimentell. De 2.5.4-experimentella versionsanteckningarna listar dem inte som åtgärdade:
- Linjalen och gradmallarna fungerar inte.
-
RenderTargetBitmapregistrerar inte bläckinmatning. Om du vill skapa en bild av bläcket sparar du det i formatetGifWithEmbeddedIsf. - Att ange en pennknapps
Paletteegenskap i XAML orsakar ett XAML-parsfel. AngePalettei koden i stället, som du ser i Anpassa en pennas färger och bredder. - Knappmenyer kan visas tomma och att ändra
ButtonFlyoutPlacementkanske inte ändrar var de öppnas.
Ändringar från 2.4.1-experimental
Om du använde pennantecknings-API:erna i Windows App SDK 2.4.1-experimental uppdaterar du koden för dessa ändringar i 2.5.4-experimental:
| API i 2.4.1-experimental | Ändring i 2.5.4-experimental |
|---|---|
InkToolbar.TargetInkPresenter (typ Object) |
Egenskapstypen är InkPresenter. |
InkPresenter.GetHighContrastAdjustment och InkPresenter.SetHighContrastAdjustment metoder |
Ersatt av InkPresenter.HighContrastAdjustment egenskapen. |
InkStrokeInput.GetInkPresenter och InkUnprocessedInput.GetInkPresenter metoder |
Ersatt av en InkPresenter egenskap för varje klass. |
InkToolbarEraserButton.SelectedEraser, IsStrokeEraserVisible, och ArePrecisionErasersVisible egenskaper; InkToolbar.EraserFlyoutItemClicked händelse; InkToolbarEraserKind, InkToolbarEraserFlyoutItemKindoch InkToolbarEraserFlyoutItemClickedEventArgs typer |
Avlägsnad. |
| Inte tillgänglig |
InkPresenter.ActivateCustomDrying metoden och InkSynchronizer klassen har lagts till. |
Tillgänglighet och hög kontrast
Digitalt bläck är visuellt innehåll, så planera hur alla användare kan använda dina bläckfunktioner:
- Ge varje arbetsyta och verktygsfält ett tillgängligt namn med
AutomationProperties.Name, som exemplen i den här artikeln gör. Ge anpassade knappar som bara visar en ikon ett namn och en knappbeskrivning. -
InkCanvasAutomationPeergör arbetsytan tillgänglig för UI Automation, men den beskriver inte penseldragen. Om pennanteckningen har betydelse, till exempel en signatur eller ett etiketterat diagram, ange ett textalternativ. - Bläckinmatning kräver pekarindata. Låt inte bläck vara det enda sättet att slutföra en uppgift. Låt till exempel användarna skriva sitt namn som ett alternativ till att signera det.
- Automatiserings-peers för de inbyggda verktygsknapparna stöder kontrollmönstren SelectionItem och ExpandCollapse. Testa tangentbordsnavigeringen och knapparnas utfällningar med den konfiguration av verktygsfältet som appen använder.
När ett kontrasttema är aktivt kan presentatören ersätta bläckfärger med systemfärger. Om du vill välja det här beteendet anger du HighContrastAdjustment egenskapen till ett InkHighContrastAdjustment värde från Microsoft.UI.Xaml.Controls namnområdet. Den Windows Runtime dokumentationen för uppräkningen med samma namn beskriver värdena på följande sätt:
| Value | Bläckfärg i ett kontrasttema |
|---|---|
UseSystemColorsWhenNecessary |
Bläckets egen färg om den kontrasterar tillräckligt med bakgrunden. Annars används en systemfärg. |
UseSystemColors |
En systemfärg. Överstrykningspennans bläck använder systemets överstrykningsfärg. |
UseOriginalColors |
Bläckets egen färg. |
inkCanvas.InkPresenter.HighContrastAdjustment = InkHighContrastAdjustment.UseSystemColorsWhenNecessary;
För att hålla dem korta anger exemplen i den här artikeln en vit bakgrund direkt. I din app definierar du ritytans bakgrund i temaordlistor så att den följer det aktiva temat enligt beskrivningen i Kontrastteman. Testa sedan appen med varje kontrasttema och kontrollera att förgrundsfärgen, bakgrunden och kantlinjen förblir urskiljbara.
Mer information finns i Exponera grundläggande hjälpmedelsinformation och Tangentbordstillgänglighet.
Se även
- WinUI 3 Gallery InkCanvas exempelkällkod
- Versionsinformation för Windows App SDK 2.5.4-experimental
- Microsoft. UI. Xaml.Controls-namnområde
- Windows.UI.Input.Inking-namnområde
- Haptisk återkoppling för penna i Windows-appar
- Penninteraktioner och Windows Ink (UWP)
- Kontroller för penninmatning (UWP)
- Vad stöds när du migrerar från UWP till WinUI 3
Windows developer