Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Important
InkCanvas, InkToolbar, dan InkPresenter adalah API eksperimental. Dukungan Inking ditambahkan dalam SDK Aplikasi Windows 2.4.1-eksperimental. Artikel ini menjelaskan SDK Aplikasi Windows 2.5.4-eksperimental, yang mengubah cara Anda menyambungkan InkToolbaraplikasi. API-API ini tidak tersedia di saluran stabil. Mereka mungkin berubah atau dihapus dalam rilis mendatang. Jangan gunakan di aplikasi produksi.
Gunakan kontrol eksperimental InkCanvas untuk memungkinkan pengguna menggambar tinta dengan pena, mouse, atau sentuhan. Tambahkan InkToolbar untuk memberi pengguna pena ballpoint, pensil, penyorot, dan penghapus, dengan opsi warna dan ukuran. Setiap kanvas memiliki InkPresenter yang mengumpulkan dan merender input tinta.
InkStrokeContainer milik presenter menyimpan goresan, sehingga Anda dapat menyimpan, memuat, menyalin, dan membuat tinta dalam kode.
Untuk mencoba kontrol, instal paket SDK Aplikasi Windows 2.5.4-eksperimental dan runtime yang cocok. Untuk tautan unduhan dan informasi penyiapan, lihat Unduhan SDK Aplikasi Windows terbaru.
Apakah ini kontrol yang tepat?
Gunakan kanvas tinta saat pengguna perlu:
- Buat catatan tulis tangan atau menganotasi konten.
- Buat sketsa, gambar diagram, atau membubuhkan tanda tangan.
- Buat konten freehand yang disimpan oleh aplikasi Anda dan dapat mereka edit nanti.
API tinta eksperimental tidak menyertakan pengenalan tulisan tangan atau pengenalan bentuk. Untuk mengumpulkan teks, gunakan kotak teks.
Jika Anda memerlukan inking di aplikasi produksi, lihat Celah kontrol yang diketahui untuk status kontrol ini di saluran stabil dan untuk alternatif.
Memahami model inking
API inking berfungsi bersama:
| API | Peranan |
|---|---|
InkCanvas |
Elemen XAML yang menampilkan tinta dan menerima input tinta. Properti InkPresenter-nya mengembalikan presenter untuk kanvas. |
InkPresenter |
Mengumpulkan input tinta, merender goresan, dan menyimpan pengaturan input dan atribut gambar default. Anda tidak dapat membuat InkPresenter sendiri. Dapatkan dari InkCanvas.InkPresenter. |
InkStrokeContainer |
Menyimpan goresan. Gunakan untuk menyimpan, memuat, memilih, menyalin, dan menempelkan tinta. Dapatkan itu dari InkPresenter.StrokeContainer. |
InkToolbar |
Toolbar opsional yang memungkinkan pengguna memilih pena, pensil, penyorot, atau penghapus, dan mengubah warna dan ukuran tinta. |
InkCanvas tidak memiliki properti Background. Untuk menambahkan latar belakang dan bingkai pada area gambar, tempatkan kanvas di dalam kontainer seperti Border, dan atur ukuran pada kontainer. Warna tinta default adalah hitam, sehingga contoh dalam artikel ini menggunakan latar belakang putih.
Beberapa jenis ini memiliki nama yang sama dengan jenis dalam namespace Windows Runtime Windows.UI.Input.Inking, seperti InkPresenter, InkStrokeContainer, dan InkHighContrastAdjustment. Kontrol penintaan menggunakan versi Microsoft.UI.Xaml.Controls tersebut. Stroke dan atribut gambar masih menggunakan jenis Windows Runtime, seperti InkStroke, InkDrawingAttributes, dan InkStrokeBuilder. Jika Anda mengimpor kedua namespace dan menggunakan nama ambigu, pengompilasi C# melaporkan kesalahan CS0104.
Contoh dalam artikel ini mengimpor Microsoft.UI.Xaml.Controls dan menggunakan arahan alias untuk jenis Windows.UI.Input.Inking yang diperlukan:
using Microsoft.UI.Xaml.Controls;
using InkDrawingAttributes = Windows.UI.Input.Inking.InkDrawingAttributes;
using InkStroke = Windows.UI.Input.Inking.InkStroke;
Anda juga dapat mengimpor kedua namespace dan menggunakan nama yang sepenuhnya memenuhi syarat untuk jenis ambigu, seperti yang dilakukan sampel Galeri WinUI 3.
InkPresenter juga menyediakan API tingkat bawah yang tidak dibahas artikel ini:
-
StrokeInputmemicu event ketika stroke dimulai, berlanjut, berakhir, atau dibatalkan. -
UnprocessedInputmemunculkan event pointer untuk input yang tidak diproses oleh penyaji sebagai tinta. -
InputConfigurationmengatur apakah penyaji memproses input dari penghapus pena dan tombol barel utama. -
InputProcessingConfigurationmengatur apakah input menggambar tinta digital, menghapus tinta, atau tidak diproses, dan bagaimana presenter menangani input seret kanan. -
ActivateCustomDryingmengembalikanInkSynchronizeruntuk aplikasi yang merender sendiri tinta yang dikomitkan.
Siapkan proyek Anda
API inking ditandai sebagai eksperimental. Saat Anda menggunakannya dalam proyek C#, pengkompilasi C# melaporkan peringatan CS8305 dan kompilator XAML melaporkan peringatan WMC1501. Jika proyek Anda memperlakukan peringatan sebagai kesalahan, peringatan ini menghentikan build.
Setelah Memutuskan untuk menggunakan API eksperimental, Anda dapat menyembunyikan peringatan ini. Tambahkan properti berikut ke file proyek C# Anda (.csproj):
<PropertyGroup>
<NoWarn>$(NoWarn);CS8305</NoWarn>
<MSBuildWarningsAsMessages>$(MSBuildWarningsAsMessages);WMC1501</MSBuildWarningsAsMessages>
</PropertyGroup>
Contoh dalam artikel ini menggunakan C#. Setiap contoh lengkap menunjukkan XAML untuk halaman dan kelas code-behind halaman, MainPage.
Buat kanvas tinta
![]()
Aplikasi Galeri WinUI 3 mencakup contoh interaktif kontrol dan fitur WinUI. Dapatkan aplikasi dari Microsoft Store atau telusuri kode sumber pada GitHub.
Contoh berikut menempatkan kanvas di dalam area putih Border. Code-behind mengatur jenis perangkat input sehingga pengguna dapat menggambar dengan pena, mouse, atau sentuhan.
<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;
}
}
Untuk mempersingkatnya, beberapa contoh selanjutnya tidak mengulangi kode ini. Atur InputDeviceTypes di setiap halaman tempat pengguna menggambar dengan mouse.
Hubungkan toolbar tinta
Tempatkan InkToolbar di dekat kanvas, dan atur properti TargetInkCanvas-nya untuk menghubungkan keduanya:
<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>
Dimulai dengan SDK Aplikasi Windows 2.5.4-eksperimental, Anda dapat menyambungkan toolbar langsung ke InkPresenter dengan mengatur properti TargetInkPresenter. Atur dalam kode:
inkToolbar.TargetInkPresenter = inkCanvas.InkPresenter;
Anda juga dapat mengaturnya di XAML: TargetInkPresenter="{x:Bind inkCanvas.InkPresenter}". Atur hanya satu dari dua properti target.
Secara default, toolbar menampilkan tombol pena, pensil, penyorot, penghapus, dan tombol stensil (penggaris). Untuk mengubah tombol mana yang muncul, lihat Menyesuaikan toolbar tinta.
Pilih perangkat input
Properti InputDeviceTypes menggunakan CoreInputDeviceTypes enumerasi bendera dari Windows.UI.Core namespace. Input dari mouse tidak membuat goresan tinta secara bawaan. Gabungkan nilai untuk memilih perangkat mana yang menggunakan tinta:
// 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);
Untuk berhenti mengumpulkan tinta dari setiap perangkat, misalnya untuk menampilkan gambar yang tidak dapat diubah pengguna, atur IsInputEnabled ke false:
inkCanvas.InkPresenter.IsInputEnabled = false;
Untuk memungkinkan pengguna mengaktifkan atau menonaktifkan perangkat input dari toolbar, lihat Menambahkan tombol pengalih kustom.
Atur atribut objek gambar
Atribut gambar, seperti warna, ukuran, dan bentuk ujung pena, mengontrol tampilan goresan. Mereka menggunakan kelas Windows RuntimeInkDrawingAttributes. Untuk mengubah atribut untuk goresan baru, panggil CopyDefaultDrawingAttributes, ubah salinan, dan teruskan ke UpdateDefaultDrawingAttributes:
InkDrawingAttributes attributes = inkCanvas.InkPresenter.CopyDefaultDrawingAttributes();
attributes.Color = Colors.DarkBlue;
attributes.Size = new Size(4, 4);
inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(attributes);
Colors berada di Microsoft.UI namespace layanan, dan Size berada di Windows.Foundation namespace layanan. Atribut baru berlaku untuk goresan yang digambar pengguna setelah panggilan berakhir. Coretan yang ada mempertahankan atributnya.
Saat Anda menghubungkan InkToolbar, alat aktif dan pengaturan warna dan ukurannya menentukan atribut gambar. Untuk membaca atribut saat ini, gunakan properti InkDrawingAttributes milik toolbar.
Buat preset pena
Jika Anda tidak menggunakan toolbar, Anda dapat membuat preset pena Anda sendiri. Contoh berikut menerapkan atribut ballpoint, pensil, highlighter, atau kaligrafi saat pengguna memilih preset atau mengubah ukuran.
<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.CreateForPencilmengembalikan atribut untuk goresan pensil. PropertiKindbersifat baca-saja, jadi gunakan metode ini untuk membuat atribut pensil. -
DrawAsHighlightermerender stroke sebagai overlay penyorot. -
PenTipmengatur bentuk ujung pena, danPenTipTransformmemutar atau menskalakannya. Tip persegi panjang yang diputar menghasilkan goresan gaya kaligrafi.
Merespons perubahan tinta
Penyaji ditampilkan StrokesCollected setelah pengguna menarik goresan dan StrokesErased setelah pengguna menghapusnya. Properti Strokes pada argumen setiap peristiwa berisi goresan yang terpengaruh. Kode berikut terus memperbarui jumlah stroke dalam TextBlock bernama 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";
}
Peristiwa ini terjadi ketika pengguna menggambar atau menghapus coretan tinta. Saat Anda mengubah stroke dalam kode, misalnya dengan Clear, LoadAsync, atau AddStroke, perbarui antarmuka pengguna Anda setelah panggilan.
Menyimpan dan memuat tinta
InkStrokeContainer menyimpan tinta dalam Ink Serialized Format (ISF). Untuk memilih bagaimana data disimpan, teruskan InkPersistenceFormat nilai ke SaveAsync:
| Format | Output |
|---|---|
GifWithEmbeddedIsf |
Gambar GIF yang berisi data tinta. Penampil gambar dapat menampilkan file, dan aplikasi Anda dapat memuat ink lagi untuk pengeditan. |
Isf |
Hanya data tinta, tanpa gambar. Ini adalah format yang paling ringkas. |
Tambahkan metode berikut ke code-behind halaman yang berisi sebuah InkCanvas bernama inkCanvas.
SaveInkAsync menulis tinta ke file, dan LoadInkAsync mengganti tinta pada kanvas dengan tinta dari file.
LoadAsync menghapus goresan yang ada sebelum menambahkan goresan yang dimuat, sehingga Anda tidak perlu memanggil Clear terlebih dahulu.
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 dan AsInputStream mengonversi aliran .NET ke jenis aliran Windows Runtime yang digunakan oleh SaveAsync dan LoadAsync. Untuk menggunakan await pada metode Windows Runtime ini, file memerlukan arahan using System;. Tanpa itu, kompilator melaporkan kesalahan CS4036.
Untuk mengizinkan pengguna memilih file, gunakan pemilih file SDK Aplikasi Windows, yang mengembalikan jalur file yang dipilih. Untuk informasi selengkapnya, lihat Menyimpan file dengan pemilih SDK Aplikasi Windows dan Membuka file dan folder dengan pemilih SDK Aplikasi Windows.
Untuk menyimpan tinta dalam memori, misalnya untuk memulihkannya nanti dalam sesi yang sama, simpan ke dalam InMemoryRandomAccessStream. Teruskan hasil GetOutputStreamAt(0) pada aliran ke SaveAsync, dan teruskan hasil GetInputStreamAt(0) pada aliran tersebut ke LoadAsync. Sampel Galeri WinUI 3 menggunakan pendekatan ini.
Menyalin dan menempelkan tinta digital
InkStrokeContainer dapat menyalin goresan ke clipboard dan menempelkan tinta darinya.
CopySelectedToClipboard menyalin hanya goresan yang dipilih, sehingga contoh berikut memilih setiap goresan di kanvas kiri sebelum menyalinnya.
CanPasteFromClipboard memeriksa apakah papan klip berisi goresan tinta digital.
PasteFromClipboard menambahkan tinta pada posisi yang Anda berikan, dan mengembalikan persegi panjang pembatas area yang berubah.
<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.";
}
}
Untuk bekerja dengan beberapa goresan, atur properti Selected dari setiap goresan yang Anda inginkan, atau panggil SelectWithLine atau SelectWithPolyLine. Untuk menghapus atau memindahkan goresan yang dipilih, gunakan DeleteSelected atau MoveSelected.
Membuat stroke dalam kode
Gunakan kelas Windows Runtime InkStrokeBuilder untuk membuat goresan dari titik, dan menambahkannya ke kanvas dengan AddStroke. Contoh berikut menambahkan bintang dan baris tanda tangan.
<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);
}
}
-
CreateStrokeFromInkPointsmembuat stroke dari nilaiInkPoint. Setiap titik menggabungkan posisi pada kanvas dengan nilai tekanan. - Dokumentasi Windows Runtime merekomendasikan
CreateStrokeFromInkPointsalih-alihCreateStrokeuntuk goresan yang Anda tambahkan ke penyaji tinta. - Ketika
FitToCurveadalahfalse, garis menggunakan segmen lurus antara titik alih-alih kurva.
Stroke hanya dapat dimiliki oleh satu wadah. Untuk menyalin goresan ke kanvas lain, tambahkan klon goresan:
private void CopyStrokesTo(InkCanvas target)
{
foreach (InkStroke stroke in inkCanvas.InkPresenter.StrokeContainer.GetStrokes())
{
target.InkPresenter.StrokeContainer.AddStroke(stroke.Clone());
}
}
Sesuaikan toolbar tinta
Pilih kontrol awal
Properti InitialControls menentukan tombol bawaan mana yang dibuat toolbar:
| Nilai | Tombol bawaan |
|---|---|
All (standar) |
Pena ballpoint, pensil, penyorot, penghapus, dan mal (penggaris) |
PensOnly |
Pulpen, pensil, dan stabilo |
AllExceptPens |
Penghapus dan stensil (penggaris) |
None |
None. Deklarasikan tombol yang Anda inginkan sebagai turunan toolbar. |
Contoh berikut mendeklarasikan empat tombol yang sama dengan sampel Galeri WinUI 3, memberi masing-masing satu nama yang dapat diakses, dan memilih pena ballpoint saat toolbar dimuat:
<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;
}
};
}
}
Untuk menampilkan perintah yang menghapus semua tinta, atur properti tombol penghapus IsClearAllVisible ke true. Toolbar memicu event EraseAllClicked saat pengguna memilih perintah.
Mengkustomisasi warna dan lebar-lebar pena
Setiap tombol pena memiliki Palette sekumpulan pengaturan kuas dan lebar goresan. Atur Palette dalam kode. Untuk detailnya, lihat Batasan yang diketahui. Contoh berikut mengganti palet pena ballpoint dengan empat warna merek, membatasi lebar goresan, dan menunjukkan atribut pena aktif.
<StackPanel Spacing="12">
<InkToolbar
x:Name="inkToolbar"
AutomationProperties.Name="Drawing tools"
InitialControls="AllExceptPens"
TargetInkCanvas="{x:Bind inkCanvas}">
<InkToolbarBallpointPenButton
x:Name="brandPen"
AutomationProperties.Name="Brand pen"
MaxStrokeWidth="16"
MinStrokeWidth="2"
SelectedStrokeWidth="6" />
</InkToolbar>
<RadioButtons
x:Name="colorButtons"
Header="Pen color"
MaxColumns="4"
SelectedIndex="0"
SelectionChanged="ColorButtons_SelectionChanged">
<x:String>Blue</x:String>
<x:String>Green</x:String>
<x:String>Orange</x:String>
<x:String>Purple</x:String>
</RadioButtons>
<TextBlock x:Name="penInfo" />
<Border
Width="480"
Height="240"
HorizontalAlignment="Left"
Background="White"
BorderBrush="{ThemeResource SystemControlForegroundChromeHighBrush}"
BorderThickness="1">
<InkCanvas x:Name="inkCanvas" AutomationProperties.Name="Drawing surface" />
</Border>
</StackPanel>
using System.Linq;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using Windows.UI;
public sealed partial class MainPage : Page
{
private static readonly Color[] BrandColors =
{
Color.FromArgb(0xFF, 0x00, 0x78, 0xD4), // Blue
Color.FromArgb(0xFF, 0x10, 0x7C, 0x10), // Green
Color.FromArgb(0xFF, 0xD8, 0x3B, 0x01), // Orange
Color.FromArgb(0xFF, 0x5C, 0x2D, 0x91), // Purple
};
public MainPage()
{
InitializeComponent();
// Replace the pen's default palette and select the first color.
brandPen.Palette = BrandColors.Select(color => (Brush)new SolidColorBrush(color)).ToList();
brandPen.SelectedBrushIndex = 0;
inkToolbar.InkDrawingAttributesChanged += (sender, args) => ShowPenInfo();
// The toolbar sets up its buttons when it loads, so select the starting tool then.
inkToolbar.Loaded += (sender, args) =>
{
inkToolbar.ActiveTool = brandPen;
ShowPenInfo();
};
}
private void ColorButtons_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
// SelectionChanged can occur while the page is still loading.
if (brandPen is null || colorButtons is null || penInfo is null || colorButtons.SelectedIndex < 0)
{
return;
}
// SelectedBrushIndex selects an entry in Palette, like choosing a color in the pen flyout.
brandPen.SelectedBrushIndex = colorButtons.SelectedIndex;
inkToolbar.ActiveTool = brandPen;
ShowPenInfo();
}
private void ShowPenInfo()
{
// InkDrawingAttributes describes the ink that the active tool draws.
if (inkToolbar.InkDrawingAttributes is { } attributes)
{
penInfo.Text = $"Color {attributes.Color}, width {attributes.Size.Width:0} (range {brandPen.MinStrokeWidth:0}-{brandPen.MaxStrokeWidth:0})";
}
}
}
SelectedBrushIndex digunakan untuk memilih warna dari Palette.
SelectedStrokeWidth, MinStrokeWidth, dan MaxStrokeWidth atur lebar goresan saat ini dan rentang yang dapat dipilih pengguna. Toolbar memicu InkDrawingAttributesChanged ketika atribut alat aktif berubah.
Pilih alat aktif
Atur ActiveTool untuk memilih alat, dan penangan ActiveToolChanged untuk merespons saat pengguna memilih alat lain.
GetToolButton mengembalikan tombol alat bawaan dari jenis yang Anda berikan. Tombol bawaan mungkin tidak ada sampai toolbar dimuat, jadi pilih alat awal di penangan peristiwa toolbar 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}";
}
}
Ubah orientasi
Untuk menampilkan toolbar secara vertikal, misalnya di samping kanvas, atur Orientation ke 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>
Properti ButtonFlyoutPlacement mengatur tempat flyout tombol terbuka relatif terhadap toolbar. Nilai default, Auto, memungkinkan toolbar memilih posisi.
Tambahkan tombol toggle kustom
Gunakan InkToolbarCustomToggleButton untuk menambahkan pengaturan yang diaktifkan dan dinonaktifkan pengguna dari toolbar. Contoh berikut menambahkan sakelar yang mengontrol input sentuh. Input pena dan mouse selalu menghasilkan coretan tinta.
<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}";
}
}
Sakelar hanya menampilkan ikon, sehingga contohnya memberinya nama yang dapat diakses dan tooltip. Dalam contoh ini, toolbar juga membuat tombol defaultnya, dan toggle kustom muncul setelah tombol tersebut.
Toolbar mendukung tombol kustom lainnya:
-
InkToolbarCustomToolButtonmenambahkan alat milik Anda sendiri. -
InkToolbarCustomPenButtonadalah pena yang membuat atribut gambarnya sendiri. Atur propertinyaCustomPenke instans kelas yang berasal dariInkToolbarCustomPendan menimpa metodeCreateInkDrawingAttributesCore.
Buat toolbar dalam kode
Anda juga dapat membuat kanvas dan toolbar dalam kode. Contoh berikut menambahkannya ke Border yang bernama inkHost, menyambungkan toolbar dengan TargetInkPresenter, dan memilih pensil saat toolbar dimuat.
<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;
}
}
Batasan yang diketahui
- Kontrol tinta digital hanya tersedia dalam rilis eksperimental SDK Aplikasi Windows. Mereka tidak tersedia di saluran stabil.
- API inking tidak menyertakan pengenalan tulisan tangan digital atau bentuk.
- Input mouse tidak menggambar tinta hingga Anda menambahkan
CoreInputDeviceTypes.MousekeInputDeviceTypes.
Masalah berikut dilaporkan dengan SDK Aplikasi Windows 2.4.1-eksperimental. Catatan rilis eksperimental 2.5.4 tidak mencantumkannya sebagai diperbaiki:
- Stensil penggaris dan protraktor tidak berfungsi.
-
RenderTargetBitmaptidak mendeteksi input tinta. Untuk membuat gambar dari tinta, simpan dalam formatGifWithEmbeddedIsf. - Mengatur properti
Palettetombol pena di XAML menyebabkan kesalahan penguraian XAML. AturPalettedalam kode sebagai gantinya, seperti yang ditunjukkan dalam Menyesuaikan warna dan lebar pena. - Flyout tombol dapat tampak kosong, dan mengubah
ButtonFlyoutPlacementmungkin tidak mengubah lokasi flyout terbuka.
Perubahan dari 2.4.1-eksperimental
Jika Anda menggunakan API penintaan di SDK Aplikasi Windows 2.4.1-eksperimental, perbarui kode Anda untuk perubahan ini dalam 2.5.4-eksperimental:
| API di 2.4.1-experimental | Perubahan pada 2.5.4-experimental |
|---|---|
InkToolbar.TargetInkPresenter (jenis Object) |
Jenis properti adalah InkPresenter. |
metode InkPresenter.GetHighContrastAdjustment dan InkPresenter.SetHighContrastAdjustment |
Digantikan oleh properti InkPresenter.HighContrastAdjustment. |
metode InkStrokeInput.GetInkPresenter dan InkUnprocessedInput.GetInkPresenter |
Digantikan oleh InkPresenter properti pada setiap kelas. |
InkToolbarEraserButton.SelectedEraser, IsStrokeEraserVisible, dan ArePrecisionErasersVisible properti; InkToolbar.EraserFlyoutItemClicked event; InkToolbarEraserKind, InkToolbarEraserFlyoutItemKind, dan InkToolbarEraserFlyoutItemClickedEventArgs tipe |
Dihapus. |
| Tidak tersedia |
InkPresenter.ActivateCustomDrying metode dan InkSynchronizer kelas ditambahkan. |
Aksesibilitas dan kontras tinggi
Tinta adalah konten visual, jadi rencanakan bagaimana semua pengguna dapat menggunakan fitur penintaan Anda:
- Beri setiap kanvas dan toolbar nama yang dapat diakses dengan
AutomationProperties.Name, seperti contoh dalam artikel ini. Beri tombol kustom yang hanya menampilkan ikon nama dan tooltip. -
InkCanvasAutomationPeermengekspos kanvas ke UI Automation, tetapi tidak menggambarkan goresan. Jika tinta memiliki arti, seperti tanda tangan atau diagram berlabel, berikan alternatif teks. - Tinta gambar memerlukan input dari perangkat penunjuk. Jangan jadikan tinta sebagai satu-satunya cara untuk menyelesaikan tugas. Misalnya, biarkan pengguna mengetikkan namanya sebagai alternatif untuk menandatangani.
- Peer otomatisasi untuk tombol alat bawaan mendukung pola kontrol SelectionItem dan ExpandCollapse. Uji navigasi keyboard dan flyout tombol dengan konfigurasi toolbar yang digunakan aplikasi Anda.
Ketika tema kontras aktif, penyaji dapat mengganti warna tinta dengan warna sistem. Untuk memilih perilaku ini, atur properti HighContrastAdjustment ke nilai InkHighContrastAdjustment dari namespace Microsoft.UI.Xaml.Controls. Dokumentasi Windows Runtime untuk enumerasi dengan nama yang sama menjelaskan nilai dengan cara ini:
| Nilai | Warna tinta dalam tema kontras |
|---|---|
UseSystemColorsWhenNecessary |
Warna tinta itu sendiri digunakan jika cukup kontras dengan latar belakang. Jika tidak, warna sistem. |
UseSystemColors |
Warna sistem. Tinta penyorot menggunakan warna penyorot sistem. |
UseOriginalColors |
Warna tintanya sendiri. |
inkCanvas.InkPresenter.HighContrastAdjustment = InkHighContrastAdjustment.UseSystemColorsWhenNecessary;
Agar tetap pendek, contoh dalam artikel ini mengatur latar belakang putih secara langsung. Di aplikasi Anda, tentukan latar belakang kanvas dalam kamus tema sehingga mengikuti tema aktif, seperti yang dijelaskan dalam tema Kontras. Kemudian uji aplikasi Anda dengan setiap tema kontras, dan periksa apakah tinta, latar belakang, dan batas tetap dapat dibedakan.
Untuk informasi selengkapnya, lihat Mengekspos informasi aksesibilitas dasar dan Aksesibilitas keyboard.
Baca juga
- Kode sumber sampel WinUI 3 Gallery InkCanvas
- Catatan rilis SDK Aplikasi Windows 2.5.4-experimental
- namespace Microsoft.UI.Xaml.Controls
- Windows.UI.Input.Inking namespace
- Umpan balik haptik pena di aplikasi Windows
- Interaksi pena dan Windows Ink (UWP)
- Kontrol tinta digital (UWP)
- Apa yang didukung saat bermigrasi dari UWP ke WinUI 3
Windows developer