共用方式為


儲存和擷取 Windows Ink 筆劃數據

支援 Windows Ink 的 Windows 應用程式可以將墨跡序列化與反序列化為 Ink 序列化格式(ISF)檔案。 ISF 檔案是 GIF 影像,其中包含所有筆墨筆劃屬性和行為的其他元數據。 未啟用筆跡的應用程式可以檢視靜態 GIF 影像,包括 Alpha 色板背景透明度。

備註

ISF 是墨跡最緊湊的持續性表示法。 它可以內嵌在二進位檔格式內,例如 GIF 檔案,或直接放在剪貼簿上。

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

重要 APIInkCanvasWindows.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. 最後,我們會在按下 [儲存] 按鈕時的 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) 檔案儲存和載入筆跡筆劃下載此範例

  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. 最後,我們在 加載 按鈕的 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 剪下和複製命令需要選取一或多個筆墨筆劃。

在此範例中,我們會在使用手寫筆桶按鈕(或滑鼠右鍵)修改輸入時啟用筆劃選擇。 如需完整範例以了解如何實作筆劃選取,請參閱 中關於進階處理的「傳遞輸入」章節,該章節涉及的筆和手寫筆互動。

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

  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. 最後,新增筆劃選取支持之後,我們會在 剪下複製貼上 按鈕的 click 事件處理程式中實作剪貼簿功能。

    針對剪下,我們會先在 inkPresenterInkPresenter 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,將剪貼簿中的墨跡筆劃插入至 InkStrokeContainerInkPresenter,然後將筆劃渲染在墨跡畫布上。

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.
        }
    }

主題範例

其他範例