支援 Windows Ink 的 Windows 應用程式可以將墨水筆劃序列化與反序列化成 Ink Serialized Format(ISF)檔案。 ISF 檔案是一張 GIF 圖片,並附有所有墨水筆觸屬性與行為的額外元資料。 不支援墨水功能的應用程式也能查看靜態 GIF 影像,包括具有 alpha 通道背景透明效果。
重要 API: InkCanvas、 Windows.UI.Input.Inking
將墨線筆劃儲存到檔案中
在這裡,我們示範如何儲存在 InkCanvas 控制項上繪製的筆觸。
下載此範例來自 保存和載入墨跡,來自 Ink Serialized Format(ISF) 檔案
首先,我們設定了介面。
介面包含「儲存」、「載入」、「清除」按鈕,以及 InkCanvas。
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="HeaderPanel" Orientation="Horizontal" Grid.Row="0">
<TextBlock x:Name="Header"
Text="Basic ink store sample"
Style="{ThemeResource HeaderTextBlockStyle}"
Margin="10,0,0,0" />
<Button x:Name="btnSave"
Content="Save"
Margin="50,0,10,0"/>
<Button x:Name="btnLoad"
Content="Load"
Margin="50,0,10,0"/>
<Button x:Name="btnClear"
Content="Clear"
Margin="50,0,10,0"/>
</StackPanel>
<Grid Grid.Row="1">
<InkCanvas x:Name="inkCanvas" />
</Grid>
</Grid>
接著我們設定一些基本的墨水輸入行為。
InkPresenter 設定為將筆與滑鼠的輸入資料解讀為墨水筆劃(InputDeviceTypes),並宣告按鈕點擊事件的監聽器。
public MainPage()
{
this.InitializeComponent();
// Set supported inking device types.
inkCanvas.InkPresenter.InputDeviceTypes =
Windows.UI.Core.CoreInputDeviceTypes.Mouse |
Windows.UI.Core.CoreInputDeviceTypes.Pen;
// Listen for button click to initiate save.
btnSave.Click += btnSave_Click;
// Listen for button click to initiate load.
btnLoad.Click += btnLoad_Click;
// Listen for button click to clear ink canvas.
btnClear.Click += btnClear_Click;
}
最後,我們將墨水儲存在 儲存 按鈕的點擊事件處理程式中。
FileSavePicker 允許使用者同時選擇檔案及儲存墨水資料的位置。
一旦選擇了檔案,我們會開啟一個 IRandomAccessStream 串流,並設定為 ReadWrite。
接著我們呼叫 SaveAsync ,將 InkStrokeContainer 管理的墨水筆劃序列化到串流中。
// Save ink data to a file.
private async void btnSave_Click(object sender, RoutedEventArgs e)
{
// Get all strokes on the InkCanvas.
IReadOnlyList<InkStroke> currentStrokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
// Strokes present on ink canvas.
if (currentStrokes.Count > 0)
{
// Let users choose their ink file using a file picker.
// Initialize the picker.
Windows.Storage.Pickers.FileSavePicker savePicker =
new Windows.Storage.Pickers.FileSavePicker();
savePicker.SuggestedStartLocation =
Windows.Storage.Pickers.PickerLocationId.DocumentsLibrary;
savePicker.FileTypeChoices.Add(
"GIF with embedded ISF",
new List<string>() { ".gif" });
savePicker.DefaultFileExtension = ".gif";
savePicker.SuggestedFileName = "InkSample";
// Show the file picker.
Windows.Storage.StorageFile file =
await savePicker.PickSaveFileAsync();
// When chosen, picker returns a reference to the selected file.
if (file != null)
{
// Prevent updates to the file until updates are
// finalized with call to CompleteUpdatesAsync.
Windows.Storage.CachedFileManager.DeferUpdates(file);
// Open a file stream for writing.
IRandomAccessStream stream = await file.OpenAsync(Windows.Storage.FileAccessMode.ReadWrite);
// Write the ink strokes to the output stream.
using (IOutputStream outputStream = stream.GetOutputStreamAt(0))
{
await inkCanvas.InkPresenter.StrokeContainer.SaveAsync(outputStream);
await outputStream.FlushAsync();
}
stream.Dispose();
// Finalize write so other apps can update file.
Windows.Storage.Provider.FileUpdateStatus status =
await Windows.Storage.CachedFileManager.CompleteUpdatesAsync(file);
if (status == Windows.Storage.Provider.FileUpdateStatus.Complete)
{
// File saved.
}
else
{
// File couldn't be saved.
}
}
// User selects Cancel and picker returns null.
else
{
// Operation cancelled.
}
}
}
備註
GIF 是唯一支援儲存墨水資料的檔案格式。 然而, LoadAsync 方法(在下一節示範)確實支援額外格式以促進向下相容。
從檔案載入墨水筆劃
在這裡,我們示範如何從檔案載入墨線描邊,並在 InkCanvas 控制項上渲染。
首先,我們設定了介面。
介面包含「儲存」、「載入」、「清除」按鈕,以及 InkCanvas。
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="HeaderPanel" Orientation="Horizontal" Grid.Row="0">
<TextBlock x:Name="Header"
Text="Basic ink store sample"
Style="{ThemeResource HeaderTextBlockStyle}"
Margin="10,0,0,0" />
<Button x:Name="btnSave"
Content="Save"
Margin="50,0,10,0"/>
<Button x:Name="btnLoad"
Content="Load"
Margin="50,0,10,0"/>
<Button x:Name="btnClear"
Content="Clear"
Margin="50,0,10,0"/>
</StackPanel>
<Grid Grid.Row="1">
<InkCanvas x:Name="inkCanvas" />
</Grid>
</Grid>
接著我們設定一些基本的墨水輸入行為。
InkPresenter 設定為將筆與滑鼠的輸入資料解讀為墨水筆劃(InputDeviceTypes),並宣告按鈕點擊事件的監聽器。
public MainPage()
{
this.InitializeComponent();
// Set supported inking device types.
inkCanvas.InkPresenter.InputDeviceTypes =
Windows.UI.Core.CoreInputDeviceTypes.Mouse |
Windows.UI.Core.CoreInputDeviceTypes.Pen;
// Listen for button click to initiate save.
btnSave.Click += btnSave_Click;
// Listen for button click to initiate load.
btnLoad.Click += btnLoad_Click;
// Listen for button click to clear ink canvas.
btnClear.Click += btnClear_Click;
}
最後,我們將墨水載入到 載入 按鈕的點擊事件處理程式中。
FileOpenPicker 允許使用者選擇檔案及取回儲存墨水資料的位置。
一旦選擇了檔案,我們會開啟一個設定為 Read 的 IRandomAccessStream 串流。
接著我們呼叫 LoadAsync 來讀取、反序列化,並將儲存的墨水筆劃載入 InkStrokeContainer。 將筆觸載入 InkStrokeContainer 後, InkPresenter 會立即將它們渲染到 InkCanvas。
備註
InkStrokeContainer 中所有現有的筆劃都會被清除,然後才會載入新的筆劃。
// Load ink data from a file.
private async void btnLoad_Click(object sender, RoutedEventArgs e)
{
// Let users choose their ink file using a file picker.
// Initialize the picker.
Windows.Storage.Pickers.FileOpenPicker openPicker =
new Windows.Storage.Pickers.FileOpenPicker();
openPicker.SuggestedStartLocation =
Windows.Storage.Pickers.PickerLocationId.DocumentsLibrary;
openPicker.FileTypeFilter.Add(".gif");
// Show the file picker.
Windows.Storage.StorageFile file = await openPicker.PickSingleFileAsync();
// User selects a file and picker returns a reference to the selected file.
if (file != null)
{
// Open a file stream for reading.
IRandomAccessStream stream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
// Read from file.
using (var inputStream = stream.GetInputStreamAt(0))
{
await inkCanvas.InkPresenter.StrokeContainer.LoadAsync(inputStream);
}
stream.Dispose();
}
// User selects Cancel and picker returns null.
else
{
// Operation cancelled.
}
}
備註
GIF 是唯一支援儲存墨水資料的檔案格式。 不過, LoadAsync 方法支援以下格式以維持向下相容。
| 格式 | Description |
|---|---|
| InkSerializedFormat | 指定使用 ISF 保存的墨水。 這是墨水中最緊湊且持久的表示方式。 它可以嵌入在二進位文件格式中,或直接放在剪貼簿上。 |
| Base64InkSerializedFormat(序列化格式) | 指定透過將 ISF 編碼為 base64 串流來持久化的墨水。 此格式可直接將墨水編碼成 XML 或 HTML 檔案。 |
| GIF | 指定以包含 ISF 作為檔案中嵌入元資料的 GIF 檔案來持久化的墨水。 這使得墨水能在未支援墨水功能的應用程式中被查看,並在返回支援墨水功能的應用程式時保持墨水的完整真實性。 此格式非常適合在 HTML 檔案中傳輸墨水內容,並讓墨水與非墨水應用程式都能使用。 |
| Base64Gif | 指定以 base64 編碼強化 GIF 持續保存的墨水。 此格式用於墨水直接編碼成 XML 或 HTML 檔案,以便日後轉換成影像。 其可能用途是生成包含所有墨水資訊的 XML 格式,並透過可擴充樣式表語言轉換(XSLT)產生 HTML。 |
用剪貼板複製貼上墨水筆劃
在這裡,我們示範如何利用剪貼簿在應用程式間轉移墨水筆劃。
為了支援剪貼簿功能,內建的 InkStrokeContainer 切割與複製指令需要選擇一個或多個墨水筆劃。
在這個例子中,當輸入被筆桿按鈕(或滑鼠右鍵)修改時,我們啟用筆劃選擇。 欲了解如何實作筆劃選擇的完整範例,請參閱筆與觸控筆互動中的進階處理直通式輸入。
從 從剪貼簿儲存和載入墨蹟下載此範例
首先,我們設定了介面。
介面包含「剪切」、「複製」、「貼上」和「清除」按鈕,以及 InkCanvas 和選取畫布。
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="HeaderPanel" Orientation="Horizontal" Grid.Row="0">
<TextBlock x:Name="tbHeader"
Text="Basic ink store sample"
Style="{ThemeResource HeaderTextBlockStyle}"
Margin="10,0,0,0" />
<Button x:Name="btnCut"
Content="Cut"
Margin="20,0,10,0"/>
<Button x:Name="btnCopy"
Content="Copy"
Margin="20,0,10,0"/>
<Button x:Name="btnPaste"
Content="Paste"
Margin="20,0,10,0"/>
<Button x:Name="btnClear"
Content="Clear"
Margin="20,0,10,0"/>
</StackPanel>
<Grid x:Name="gridCanvas" Grid.Row="1">
<!-- Canvas for displaying selection UI. -->
<Canvas x:Name="selectionCanvas"/>
<!-- Inking area -->
<InkCanvas x:Name="inkCanvas"/>
</Grid>
</Grid>
接著我們設定一些基本的墨水輸入行為。
InkPresenter 被設定為將筆與滑鼠的輸入資料解讀為筆劃(InputDeviceTypes)。 按鈕點擊事件的監聽器,以及用於選取功能的指標與筆劃事件也在此宣告。
欲了解如何實作筆劃選擇的完整範例,請參閱筆 與觸控筆互動中進階處理的直通輸入。
public MainPage()
{
this.InitializeComponent();
// Set supported inking device types.
inkCanvas.InkPresenter.InputDeviceTypes =
Windows.UI.Core.CoreInputDeviceTypes.Mouse |
Windows.UI.Core.CoreInputDeviceTypes.Pen;
// Listen for button click to cut ink strokes.
btnCut.Click += btnCut_Click;
// Listen for button click to copy ink strokes.
btnCopy.Click += btnCopy_Click;
// Listen for button click to paste ink strokes.
btnPaste.Click += btnPaste_Click;
// Listen for button click to clear ink canvas.
btnClear.Click += btnClear_Click;
// By default, the InkPresenter processes input modified by
// a secondary affordance (pen barrel button, right mouse
// button, or similar) as ink.
// To pass through modified input to the app for custom processing
// on the app UI thread instead of the background ink thread, set
// InputProcessingConfiguration.RightDragAction to LeaveUnprocessed.
inkCanvas.InkPresenter.InputProcessingConfiguration.RightDragAction =
InkInputRightDragAction.LeaveUnprocessed;
// Listen for unprocessed pointer events from modified input.
// The input is used to provide selection functionality.
inkCanvas.InkPresenter.UnprocessedInput.PointerPressed +=
UnprocessedInput_PointerPressed;
inkCanvas.InkPresenter.UnprocessedInput.PointerMoved +=
UnprocessedInput_PointerMoved;
inkCanvas.InkPresenter.UnprocessedInput.PointerReleased +=
UnprocessedInput_PointerReleased;
// Listen for new ink or erase strokes to clean up selection UI.
inkCanvas.InkPresenter.StrokeInput.StrokeStarted +=
StrokeInput_StrokeStarted;
inkCanvas.InkPresenter.StrokesErased +=
InkPresenter_StrokesErased;
}
最後,在新增筆劃選取支援後,我們在 剪切、 複製和 貼上 按鈕的點擊事件處理程式中實作剪貼板功能。
對於切割,我們首先呼叫 InkPresenter 的 InkStrokeContainer 上的 CopySelectedToClipboard。
接著我們呼叫 DeleteSelect ,將筆觸從墨水畫布中移除。
最後,將選取畫布中的所有選取筆劃刪除。
private void btnCut_Click(object sender, RoutedEventArgs e)
{
inkCanvas.InkPresenter.StrokeContainer.CopySelectedToClipboard();
inkCanvas.InkPresenter.StrokeContainer.DeleteSelected();
ClearSelection();
}
// Clean up selection UI.
private void ClearSelection()
{
var strokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
foreach (var stroke in strokes)
{
stroke.Selected = false;
}
ClearDrawnBoundingRect();
}
private void ClearDrawnBoundingRect()
{
if (selectionCanvas.Children.Any())
{
selectionCanvas.Children.Clear();
boundingRect = Rect.Empty;
}
}
對於複製,我們只需在 InkPresenter 的 InkStrokeContainer 上呼叫 CopySelectedToClipboard。
private void btnCopy_Click(object sender, RoutedEventArgs e)
{
inkCanvas.InkPresenter.StrokeContainer.CopySelectedToClipboard();
}
對於貼上,我們會呼叫 CanPasteFromClipboard ,以確保剪貼板上的內容能貼到墨水畫布上。
如果是這樣,我們會呼叫 PasteFromClipboard,將剪貼板的筆觸插入 InkPresenter 的 InkStrokeContainer,然後再將筆觸渲染到墨水畫布上。
private void btnPaste_Click(object sender, RoutedEventArgs e)
{
if (inkCanvas.InkPresenter.StrokeContainer.CanPasteFromClipboard())
{
inkCanvas.InkPresenter.StrokeContainer.PasteFromClipboard(
new Point(0, 0));
}
else
{
// Cannot paste from clipboard.
}
}
相關文章
主題範例
其他取樣