儲存和擷取 Windows Ink 筆劃資料 \(部分機器翻譯\)

支援 Windows Ink 的 Windows 應用程式可以將筆跡筆畫序列化和反序列化為 Ink 序列化格式 (ISF) 檔案。 ISF 檔案是 GIF 影像,其中包含所有筆墨筆劃屬性和行為的其他元資料。 不支援筆跡的應用程式可以查看靜態 GIF 影像,包括 Alpha 通道背景透明度。+

注意

ISF 是筆跡最精簡的持續性表示法。 它可以嵌入二進位文件格式 (例如 GIF 檔案),或直接放置在剪貼簿上。

您可以從 Microsoft 下載中心下載筆跡序列化格式 (ISF) 規格。

重要 API: InkCanvasWindows.UI.Input.Inking

將筆跡筆劃儲存至檔案

在這裡,我們將示範如何儲存 InkCanvas 控制項上繪製的筆墨筆劃。

從筆跡序列化格式 (ISF) 檔案儲存和載入筆跡筆畫下載此範例

  1. 首先,我們會設定 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>
  1. 然後,我們會設定一些基本的筆跡輸入行為。

    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;
    }
  1. 最後,我們將筆跡保存在儲存按鈕的點擊事件處理程序中。

    FileSavePicker 可讓使用者同時選取檔案和儲存筆跡資料的位置。

    選擇檔案後,我們開啟一個設定為 ReadWriteIRandomAccessStream 流。

    然後,我們呼叫 SaveAsyncInkStrokeContainer 管理的筆跡筆畫序列化到流中。

// 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) 檔案儲存和載入筆跡筆畫下載此範例

  1. 首先,我們會設定 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>
  1. 然後,我們會設定一些基本的筆跡輸入行為。

    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;
    }
  1. 最後,我們在載入按鈕的點擊事件處理程序中載入筆跡。

    FileOpenPicker 可讓使用者從何處選取檔案和位置,以擷取儲存的筆跡資料。

    選擇檔案後,我們打開一個設定為 ReadIRandomAccessStream 流。

    然後,我們呼叫 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 指定透過使用包含 ISF 作為嵌入在檔案中的元資料的 GIF 檔案來保留的筆跡。 這使得可以在未啟用筆跡的應用程式中查看筆跡,並在返回啟用筆跡的應用程式時保持其完整的筆跡保真度。 當在 HTML 檔案中傳輸筆跡內容並使其可供筆跡和非筆跡應用程式使用時,此格式非常理想。
Base64Gif 指定使用 Base64 編碼的強化 GIF 保留的筆跡。 當筆跡要直接在 XML 或 HTML 檔案中編碼,以便稍後轉換成影像時,會提供此格式。 其可能的用途是產生 XML 格式以包含所有筆跡訊息,並用於透過可擴展樣式表語言轉換 (XSLT) 產生 HTML。

使用剪貼簿複製並貼上筆跡筆劃

在這裡,我們示範如何使用剪貼簿在應用程式之間傳輸筆跡筆畫。

為了支援剪貼簿功能,內建 InkStrokeContainer 剪下和複製指令需要選擇一個或多個筆跡筆畫。

在此範例中,當使用筆筒按鈕 (或滑鼠右鍵) 修改輸入時,我們啟用筆畫選擇。 有關如何實現筆畫選擇的完整範例,請參閱筆和手寫筆互動中的高級處理的直通輸入。

從剪貼簿儲存並載入筆跡筆畫下載此範例

  1. 首先,我們會設定 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>
  1. 然後,我們會設定一些基本的筆跡輸入行為。

    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;
    }
  1. 最後,在新增筆畫選擇支援之後,我們在剪下複製貼上按鈕的點擊事件處理程序中實現了剪貼簿功能。

    對於剪切,我們首先在 InkPresenterInkPresenter 上呼叫 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;
        }
    }

對於複製,我們只需在 InkPresenterInkStrokeContainer 上呼叫 CopySelectedToClipboard 即可。

private void btnCopy_Click(object sender, RoutedEventArgs e)
    {
        inkCanvas.InkPresenter.StrokeContainer.CopySelectedToClipboard();
    }

為了貼上,我們呼叫 CanPasteFromClipboard 以確保剪貼簿上的內容可以貼到筆跡畫布上。

如果是這樣,我們呼叫 PasteFromClipboard 將剪貼簿筆跡筆畫插入到 InkPresenter 的 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.
        }
    }

主題範例

其他範例