支援 Windows Ink 的 Windows 應用程式可以將墨跡序列化與反序列化為 Ink 序列化格式(ISF)檔案。 ISF 檔案是 GIF 影像,其中包含所有筆墨筆劃屬性和行為的其他元數據。 未啟用筆跡的應用程式可以檢視靜態 GIF 影像,包括 Alpha 色板背景透明度。
重要 API: InkCanvas、 Windows.UI.Input.Inking
將筆劃儲存至檔案
在這裡,我們將示範如何儲存在 InkCanvas 控制項
從 從筆跡串行化格式 (ISF) 檔案儲存和載入筆跡筆劃下載此範例
首先,我們會設定UI。
UI 包含 [儲存]、[載入] 和 [清除] 按鈕,以及 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;
}
最後,我們會在按下 [儲存] 按鈕時的 click 事件處理程式中儲存筆跡。
FileSavePicker 可讓用戶同時選取檔案和儲存筆跡數據的位置。
選取檔案之後,我們會開啟設定為 ReadWriteIRandomAccessStream 數據流。
然後,我們呼叫 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 控件上轉譯。
從 從筆跡串行化格式 (ISF) 檔案儲存和載入筆跡筆劃下載此範例
首先,我們會設定UI。
UI 包含 [儲存]、[載入] 和 [清除] 按鈕,以及 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;
}
最後,我們在 加載 按鈕的 click 事件處理程式中載入筆跡。
FileOpenPicker 可讓用戶同時選擇檔案以及選擇從哪裡提取已儲存的筆跡數據。
選取檔案之後,我們會開啟 IRandomAccessStream 資料流設定為 Read。
接著呼叫 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 方法支援下列格式以提供回溯相容性。
格式 | 說明 |
---|---|
InkSerializedFormat | 指定使用 ISF 格式持久化存儲的筆跡。 最精簡的筆跡持久性表示法。 它可以內嵌在二進位檔格式內,或直接放在剪貼簿上。 |
Base64InkSerializedFormat | 指定將ISF編碼為base64數據流所保存的筆跡。 提供此格式,以便直接在 XML 或 HTML 檔案中編碼筆跡。 |
圖形交換格式 (Gif) | 指定使用 GIF 檔案保存的筆跡,該檔案包含 ISF 作為內嵌在檔案中的元數據。 這可讓墨跡在未啟用墨跡的應用程式中檢視,並在回到已啟用墨跡的應用程式時,維持其完整的墨跡忠實度。 在 HTML 檔案中傳輸筆跡內容,並使它可供筆跡和非筆跡應用程式使用時,此格式很理想。 |
Base64Gif | 指定使用base64編碼的強化GIF保存的筆跡。 當筆跡要直接在 XML 或 HTML 檔案中編碼,以便稍後轉換成影像時,會提供此格式。 可能的用法是透過 XML 格式產生包含所有墨水資訊的文件,並利用可擴展樣式表語言轉換(XSLT)來生成 HTML。 |
使用剪貼簿來複製和貼上墨跡筆劃
在此,我們將演示如何使用剪貼板在應用程式之間傳輸筆跡。
為了支援剪貼簿功能,內建的 InkStrokeContainer 剪下和複製命令需要選取一或多個筆墨筆劃。
在此範例中,我們會在使用手寫筆桶按鈕(或滑鼠右鍵)修改輸入時啟用筆劃選擇。 如需完整範例以了解如何實作筆劃選取,請參閱 中關於進階處理的「傳遞輸入」章節,該章節涉及的筆和手寫筆互動。
儲存並從剪貼簿載入此範例 從剪貼簿儲存和載入筆跡
首先,我們會設定UI。
UI 包含「剪下」、「複製」、「貼上」和「清除」按鈕,以及 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)。 這裡也會宣告按鈕上 Click 事件的接聽程式,以及選取功能的指標和筆劃事件。
如需完整範例以了解如何實作筆劃選取,請參閱 中關於進階處理的「傳遞輸入」章節,該章節涉及的筆和手寫筆互動。
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;
}
最後,新增筆劃選取支持之後,我們會在 剪下、複製和 貼上 按鈕的 click 事件處理程式中實作剪貼簿功能。
針對剪下,我們會先在 inkPresenter
InkPresenter 上呼叫InkStrokeContainer 。CopySelectedToClipboard 然後,我們會呼叫 DeleteSelected,以從筆跡畫布中移除筆劃。
最後,我們會從選取畫布中刪除所有選擇筆劃。
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,將剪貼簿中的墨跡筆劃插入至 InkStrokeContainer 的 InkPresenter,然後將筆劃渲染在墨跡畫布上。
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.
}
}
相關文章
主題範例
其他範例