Windows Ink のストロークデータを格納および取得する

Windows Ink をサポートするWindows アプリは、インク ストロークをシリアル化して、インク シリアル化形式 (ISF) ファイルに逆シリアル化できます。 ISF ファイルは GIF イメージであり、すべてのインク ストロークのプロパティと動作のメタデータが追加されています。 インク対応ではないアプリでは、アルファ チャネルの背景の透明度など、静的 GIF イメージを表示できます。

注

ISF は、インクの最もコンパクトな永続的表現です。 GIF ファイルなどのバイナリ ドキュメント形式で埋め込んだり、クリップボードに直接配置したりできます。

インク シリアル化形式 (ISF) 仕様は、 Microsoft ダウンロード センターからダウンロードできます。

重要なAPI: InkCanvas, Windows.UI.Input.Inking

インク ストロークをファイルに保存する

ここでは、InkCanvas コントロールに描画されたインク ストロークを保存する方法を示します。

インク シリアル化形式 (ISF) ファイルからインク ストロークを保存して読み込む方法からこのサンプルをダウンロードします

  1. まず、UI を設定します。

    UI には、"Save"、"Load"、および "Clear" ボタン、および 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 コントロールにレンダリングする方法を示します。

インク シリアル化形式 (ISF) ファイルからインク ストロークを保存して読み込む方法からこのサンプルをダウンロードします

  1. まず、UI を設定します。

    UI には、"Save"、"Load"、および "Clear" ボタン、および 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 を使用すると、ユーザーはファイルと保存されたインク データを取得する場所の両方を選択できます。

    ファイルが選択されたら、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 を使用して永続化されるインクを指定します。 これは、インクの最もコンパクトな永続的な表現です。 バイナリ ドキュメント形式で埋め込んだり、クリップボードに直接配置したりできます。
Base64インクシリアライズドフォーマット ISF を base64 ストリームとしてエンコードすることによって永続化されるインクを指定します。 この形式は、XML または HTML ファイルでインクを直接エンコードできるように提供されます。
Gif (ジフ) ファイル内に埋め込まれたメタデータとして ISF を含む GIF ファイルを使用して永続化されるインクを指定します。 これにより、インクが有効になっていないアプリケーションでインクを表示し、インク対応アプリケーションに戻ったときに完全なインクの忠実性を維持できます。 この形式は、HTML ファイル内でインク コンテンツを転送する場合や、インクアプリケーションやインク以外のアプリケーションで使用できる場合に最適です。
Base64Gif base64 でエンコードされた強化 GIF を使用して永続化されるインクを指定します。 この形式は、後で画像に変換するために、インクを XML または HTML ファイルに直接エンコードする場合に提供されます。 これを使用するには、すべてのインク情報を含めるために生成され、拡張スタイルシート言語変換 (XSLT) を介して HTML を生成するために使用される XML 形式で使用できます。

インク ストロークをクリップボードにコピーして貼り付ける

ここでは、クリップボードを使用してアプリ間でインク ストロークを転送する方法を示します。

クリップボード機能をサポートするには、組み込みの InkStrokeContainer 切り取りコマンドとコピー コマンドで 1 つ以上のインク ストロークを選択する必要があります。

この例では、ペン バレル ボタン (またはマウスの右ボタン) で入力が変更されたときにストロークの選択を有効にします。 ストロークの選択を実装する方法の完全な例については、「 Pen とスタイラスの操作での高度な処理のパススルー入力」を参照してください。

[ 保存] からこのサンプルをダウンロードし、クリップボードからインク ストロークを読み込む

  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) として解釈するように構成されています。 ボタンのクリックイベントと選択機能のためのポインターイベントおよびストロークイベントに対するリスナーもここで宣言されています。

    ストロークの選択を実装する方法の完全な例については、「 Pen とスタイラスの操作での高度な処理のパススルー入力」を参照してください。

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. 最後に、ストローク選択のサポートを追加した後、 切り取り、 コピー、 貼り付け ボタンのクリック イベント ハンドラーにクリップボード機能を実装します。

    cutの場合、最初にCopySelectedToClipboardをInkStrokeContainerのInkPresenterで呼び出します。

    次に 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;
        }
    }

コピーの場合は、InkStrokeContainer の InkPresenter に対して、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.
        }
    }

トピックのサンプル

その他のサンプル