共用方式為


儲存與檢索 Windows Ink 筆劃資料

支援 Windows Ink 的 Windows 應用程式可以將墨水筆劃序列化與反序列化成 Ink Serialized Format(ISF)檔案。 ISF 檔案是一張 GIF 圖片,並附有所有墨水筆觸屬性與行為的額外元資料。 不支援墨水功能的應用程式也能查看靜態 GIF 影像,包括具有 alpha 通道背景透明效果。

備註

ISF 是最緊湊且持久的墨水表示方式。 它可以嵌入於二進位文件格式中,例如 GIF 檔案,或直接放置在剪貼簿上。

墨水序列化格式(ISF)規範可從 Microsoft 下載中心下載。

重要 APIInkCanvasWindows.UI.Input.Inking

將墨線筆劃儲存到檔案中

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

下載此範例來自 保存和載入墨跡,來自 Ink Serialized Format(ISF) 檔案

  1. 首先,我們設定了介面。

    介面包含「儲存」、「載入」、「清除」按鈕,以及 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 允許使用者同時選擇檔案及儲存墨水資料的位置。

    一旦選擇了檔案,我們會開啟一個 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 控制項上渲染。

Ink Serialized Format(ISF) 檔案中儲存和載入墨跡 下載此範例

  1. 首先,我們設定了介面。

    介面包含「儲存」、「載入」、「清除」按鈕,以及 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 方法支援以下格式以維持向下相容。

格式 Description
InkSerializedFormat 指定使用 ISF 保存的墨水。 這是墨水中最緊湊且持久的表示方式。 它可以嵌入在二進位文件格式中,或直接放在剪貼簿上。
Base64InkSerializedFormat(序列化格式) 指定透過將 ISF 編碼為 base64 串流來持久化的墨水。 此格式可直接將墨水編碼成 XML 或 HTML 檔案。
GIF 指定以包含 ISF 作為檔案中嵌入元資料的 GIF 檔案來持久化的墨水。 這使得墨水能在未支援墨水功能的應用程式中被查看,並在返回支援墨水功能的應用程式時保持墨水的完整真實性。 此格式非常適合在 HTML 檔案中傳輸墨水內容,並讓墨水與非墨水應用程式都能使用。
Base64Gif 指定以 base64 編碼強化 GIF 持續保存的墨水。 此格式用於墨水直接編碼成 XML 或 HTML 檔案,以便日後轉換成影像。 其可能用途是生成包含所有墨水資訊的 XML 格式,並透過可擴充樣式表語言轉換(XSLT)產生 HTML。

用剪貼板複製貼上墨水筆劃

在這裡,我們示範如何利用剪貼簿在應用程式間轉移墨水筆劃。

為了支援剪貼簿功能,內建的 InkStrokeContainer 切割與複製指令需要選擇一個或多個墨水筆劃。

在這個例子中,當輸入被筆桿按鈕(或滑鼠右鍵)修改時,我們啟用筆劃選擇。 欲了解如何實作筆劃選擇的完整範例,請參閱筆與觸控筆互動中的進階處理直通式輸入。

從剪貼簿儲存和載入墨蹟下載此範例

  1. 首先,我們設定了介面。

    介面包含「剪切」、「複製」、「貼上」和「清除」按鈕,以及 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)。 按鈕點擊事件的監聽器,以及用於選取功能的指標與筆劃事件也在此宣告。

    欲了解如何實作筆劃選擇的完整範例,請參閱筆 與觸控筆互動中進階處理的直通輸入。

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. 最後,在新增筆劃選取支援後,我們在 剪切複製貼上 按鈕的點擊事件處理程式中實作剪貼板功能。

    對於切割,我們首先呼叫 InkPresenterInkStrokeContainer 上的 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;
        }
    }

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

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

對於貼上,我們會呼叫 CanPasteFromClipboard ,以確保剪貼板上的內容能貼到墨水畫布上。

如果是這樣,我們會呼叫 PasteFromClipboard,將剪貼板的筆觸插入 InkPresenterInkStrokeContainer,然後再將筆觸渲染到墨水畫布上。

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

主題範例

其他取樣