Menyimpan dan mengambil data stroke Windows Ink

Aplikasi Windows yang mendukung Windows Ink dapat menserialisasikan dan mendeserialisasi goresan tinta ke file Ink Serialized Format (ISF). File ISF adalah gambar GIF dengan metadata tambahan untuk semua properti dan perilaku goresan tinta. Aplikasi yang tidak mendukung tinta, dapat melihat gambar GIF statis, termasuk transparansi latar belakang saluran alfa.

Catatan

ISF adalah representasi tinta yang paling ringkas. Ini dapat disematkan dalam format dokumen biner, seperti file GIF, atau ditempatkan langsung di Clipboard.

Spesifikasi Format Serial Tinta (ISF) dapat diunduh dari Pusat Unduhan Microsoft.

API Penting: InkCanvas, Windows.UI.Input.Inking

Menyimpan goresan tinta ke file

Di sini, kami menunjukkan cara menyimpan goresan tinta yang digambar pada kontrol InkCanvas .

Unduh sampel ini dari Simpan dan muat goresan tinta dari file Ink Serialized Format (ISF)

  1. Pertama, kami menyiapkan UI.

    UI mencakup tombol "Simpan", "Muat", dan "Hapus", dan 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. Kami kemudian mengatur beberapa perilaku input tinta dasar.

    InkPresenter dikonfigurasi untuk menginterpretasikan data input dari pena dan mouse sebagai goresan tinta (InputDeviceTypes), dan pendengar untuk peristiwa klik pada tombol dinyatakan.

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. Terakhir, kami menyimpan tinta di penanganan aktivitas klik tombol Simpan .

    FileSavePicker memungkinkan pengguna memilih file dan lokasi tempat data tinta disimpan.

    Setelah file dipilih, kami membuka aliran IRandomAccessStream yang diatur ke ReadWrite.

    Kami kemudian memanggil SaveAsync untuk menserialisasikan goresan tinta yang dikelola oleh InkStrokeContainer ke aliran.

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

Catatan

GIF adalah satu-satunya format file yang didukung untuk menyimpan data tinta. Namun, metode LoadAsync (ditunjukkan di bagian berikutnya) mendukung format tambahan untuk kompatibilitas mundur.

Memuat goresan tinta dari file

Di sini, kami menunjukkan cara memuat goresan tinta dari file dan merendernya pada kontrol InkCanvas .

Unduh sampel ini dari Simpan dan muat goresan tinta dari file Ink Serialized Format (ISF)

  1. Pertama, kami menyiapkan UI.

    UI mencakup tombol "Simpan", "Muat", dan "Hapus", dan 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. Kami kemudian mengatur beberapa perilaku input tinta dasar.

    InkPresenter dikonfigurasi untuk menginterpretasikan data input dari pena dan mouse sebagai goresan tinta (InputDeviceTypes), dan pendengar untuk peristiwa klik pada tombol dinyatakan.

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. Terakhir, kami memuat tinta di penanganan aktivitas klik tombol Muat .

    FileOpenPicker memungkinkan pengguna memilih file dan lokasi tempat mengambil data tinta yang disimpan.

    Setelah file dipilih, kami membuka aliran IRandomAccessStream yang diatur ke Baca.

    Kami kemudian memanggil LoadAsync untuk membaca, mendeserialisasikan, dan memuat goresan tinta yang disimpan ke inkStrokeContainer. Memuat stroke ke InkStrokeContainer menyebabkan InkPresenter segera merendernya ke InkCanvas.

    Catatan

    Semua stroke yang ada di InkStrokeContainer dibersihkan sebelum stroke baru dimuat.

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

Catatan

GIF adalah satu-satunya format file yang didukung untuk menyimpan data tinta. Namun, metode LoadAsync memang mendukung format berikut untuk kompatibilitas mundur.

Format Deskripsi
InkSerializedFormat Menentukan tinta yang bertahan menggunakan ISF. Ini adalah representasi tinta yang paling ringkas. Ini dapat disematkan dalam format dokumen biner atau ditempatkan langsung di Clipboard.
Base64InkSerializedFormat Menentukan tinta yang bertahan dengan mengodekan ISF sebagai aliran base64. Format ini disediakan sehingga tinta dapat dikodekan langsung dalam file XML atau HTML.
Gif Menentukan tinta yang bertahan dengan menggunakan file GIF yang berisi ISF sebagai metadata yang disematkan dalam file. Ini memungkinkan tinta untuk dilihat dalam aplikasi yang tidak diaktifkan tinta dan mempertahankan keakuratan tinta penuhnya ketika kembali ke aplikasi yang mendukung tinta. Format ini sangat ideal saat mengangkut konten tinta dalam file HTML dan untuk membuatnya dapat digunakan oleh aplikasi tinta dan non-tinta.
Base64Gif Menentukan tinta yang bertahan dengan menggunakan GIF fortified yang dikodekan base64. Format ini disediakan ketika tinta akan dikodekan langsung dalam file XML atau HTML untuk konversi nanti menjadi gambar. Kemungkinan penggunaan ini dalam format XML yang dihasilkan untuk berisi semua informasi tinta dan digunakan untuk menghasilkan HTML melalui Transformasi Bahasa Lembar Gaya yang Dapat Diperluas (XSLT).

Salin dan tempel goresan tinta dengan clipboard

Di sini, kami menunjukkan cara menggunakan clipboard untuk mentransfer goresan tinta antar aplikasi.

Untuk mendukung fungsionalitas clipboard, perintah potong dan salin InkStrokeContainer bawaan memerlukan satu atau beberapa goresan tinta yang dipilih.

Untuk contoh ini, kami mengaktifkan pemilihan stroke ketika input dimodifikasi dengan tombol laras pena (atau tombol kanan mouse). Untuk contoh lengkap tentang cara menerapkan pemilihan stroke, lihat Input pass-through untuk pemrosesan lanjutan dalam interaksi Pena dan stylus.

Unduh sampel ini dari Simpan dan muat goresan tinta dari clipboard

  1. Pertama, kami menyiapkan UI.

    UI mencakup tombol "Potong", "Salin", "Tempel", dan "Hapus", bersama dengan InkCanvas dan kanvas pilihan.

<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. Kami kemudian mengatur beberapa perilaku input tinta dasar.

    InkPresenter dikonfigurasi untuk menginterpretasikan data input dari pena dan mouse sebagai goresan tinta (InputDeviceTypes). Pendengar untuk peristiwa klik pada tombol serta peristiwa pointer dan goresan untuk fungsionalitas pemilihan juga dinyatakan di sini.

    Untuk contoh lengkap tentang cara menerapkan pemilihan stroke, lihat Input pass-through untuk pemrosesan lanjutan dalam interaksi Pena dan stylus.

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. Terakhir, setelah menambahkan dukungan pemilihan stroke, kami menerapkan fungsionalitas clipboard di klik penanganan aktivitas tombol Potong, Salin, dan Tempel .

    Untuk memotong, pertama-tama kami memanggil CopySelectedToClipboard di InkStrokeContainer dari InkPresenter.

    Kami kemudian memanggil DeleteSelected untuk menghilangkan stroke dari kanvas tinta.

    Terakhir, kami menghapus semua goresan pilihan dari kanvas pilihan.

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

Untuk penyalinan, kami hanya memanggil CopySelectedToClipboard di InkStrokeContainer dari InkPresenter.

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

Untuk tempel, kami memanggil CanPasteFromClipboard untuk memastikan bahwa konten pada clipboard dapat ditempelkan ke kanvas tinta.

Jika demikian, kami memanggil PasteFromClipboard untuk memasukkan goresan tinta clipboard ke inkStrokeContainer dari InkPresenter, yang kemudian merender goresan ke kanvas tinta.

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

Sampel topik

Sampel lainnya