Udostępnij przez


Przechowywanie i pobieranie danych pociągnięć rysików Windows Ink

Aplikacje systemu Windows, które obsługują Windows Ink, mogą serializować i deserializować pociągnięcia piórem do pliku Ink Serialized Format (ISF). Plik ISF jest obrazem GIF z dodatkowymi metadanymi dla wszystkich właściwości i zachowań pociągnięć atramentu. Aplikacje, które nie obsługują funkcji pisma cyfrowego, mogą wyświetlać statyczny obrazek GIF, w tym przezroczystość tła z kanałem alfa.

Uwaga / Notatka

ISF to najbardziej kompaktowa trwała reprezentacja atramentu. Można go osadzić w formacie dokumentu binarnego, takim jak plik GIF, lub umieścić bezpośrednio w Schowku.

Specyfikację formatu serializowanego pisma odkowego (ISF) można pobrać z Centrum pobierania Microsoft.

Ważne interfejsy API: InkCanvas, Windows.UI.Input.Inking

Zapisywanie pociągnięć tuszem do pliku

W tym miejscu demonstrujemy, jak zapisać pociągnięcia atramentu rysowane na kontrolce InkCanvas.

Pobierz ten przykład z pliku "Zapisywanie i ładowanie pociągnięć atramentu z pliku w formacie Ink Serialized Format (ISF)"

  1. Najpierw skonfigurujemy interfejs użytkownika.

    Interfejs użytkownika zawiera przyciski "Zapisz", "Załaduj" i "Wyczyść" oraz 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. Następnie ustawiamy niektóre podstawowe zachowania danych wejściowych atramentu.

    InkPresenter jest skonfigurowany do interpretowania danych wejściowych zarówno z pióra, jak i myszy jako pociągnięć atramentu (InputDeviceTypes), a odbiorniki zdarzeń kliknięcia przycisków są deklarowane.

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. Na koniec zapiszemy atrament w procedurze obsługi zdarzeń kliknięcia przycisku Zapisz .

    FileSavePicker pozwala użytkownikowi wybrać zarówno plik, jak i lokalizację, w której zapisywane są dane atramentu.

    Po wybraniu pliku otworzymy strumień IRandomAccessStream ustawiony na ReadWrite.

    Następnie wywołujemy SaveAsync, aby serializować pociągnięcia atramentem zarządzane przez InkStrokeContainer do strumienia.

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

Uwaga / Notatka

Plik GIF jest jedynym formatem pliku obsługiwanym do zapisywania danych pisma odkowego. Jednak metoda LoadAsync (pokazana w następnej sekcji) obsługuje dodatkowe formaty zgodności z poprzednimi wersjami.

Ładowanie pociągnięć atramentowych z pliku

W tym miejscu pokazano, jak ładować pociągnięcia pisma odręcznego z pliku i renderować je na kontrolce InkCanvas .

Pobierz ten przykład z Zapisywanie i ładowanie pociągnięć atramentu z pliku w formacie Ink Serialized Format (ISF)

  1. Najpierw skonfigurujemy interfejs użytkownika.

    Interfejs użytkownika zawiera przyciski "Zapisz", "Załaduj" i "Wyczyść" oraz 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. Następnie ustawiamy niektóre podstawowe zachowania danych wejściowych atramentu.

    InkPresenter jest skonfigurowany do interpretowania danych wejściowych zarówno z pióra, jak i z myszy jako pociągnięć atramentu (InputDeviceTypes), a odbiorniki zdarzeń kliknięcia na przyciskach zostały zadeklarowane.

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. Na koniec ładujemy tusz w obsłudze zdarzenia kliknięcia przycisku Załaduj.

    Funkcja FileOpenPicker umożliwia użytkownikowi wybranie zarówno pliku, jak i lokalizacji, z której mają zostać pobrane zapisane dane pisma odręcznego.

    Po wybraniu pliku otworzymy strumień IRandomAccessStream ustawiony na Odczyt.

    Następnie wywołamy metodę LoadAsync, aby odczytać, deserializować i załadować zapisane pociągnięcia atramentu do InkStrokeContainer. Ładowanie pociągnięć do InkStrokeContainer powoduje, że InkPresenter natychmiast renderuje je na InkCanvas.

    Uwaga / Notatka

    Wszystkie istniejące pociągnięcia w InkStrokeContainer są czyszczone przed załadowaniem nowych pociągnięć.

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

Uwaga / Notatka

Plik GIF jest jedynym formatem obsługiwanym do zapisywania danych atramentu. Jednak metoda LoadAsync obsługuje następujące formaty w celu zapewnienia zgodności z poprzednimi wersjami.

Format Description
InkSerializedFormat Określa atrament, który jest utrwalany z wykorzystaniem ISF. Jest to najbardziej kompaktowa trwała reprezentacja tuszu. Można ją osadzić w formacie dokumentu binarnego lub umieścić bezpośrednio w Schowku.
Base64InkSerializedFormat Określa atrament, który jest utrwalany przez kodowanie isF jako strumienia base64. Ten format jest dostarczany, dzięki czemu atrament może być zakodowany bezpośrednio w pliku XML lub HTML.
GIF Określa atrament, który jest utrwalany przy użyciu pliku GIF, który zawiera isF jako metadane osadzone w pliku. Dzięki temu atrament można wyświetlać w aplikacjach, które nie obsługują atramentu, i zachować pełną wierność atramentu po powrocie do aplikacji obsługującej atrament. Format ten jest idealny do transportowania zawartości atramentu w pliku HTML i umożliwia jego użycie w aplikacjach wykorzystujących atrament i tych, które go nie wykorzystują.
Base64Gif Określa atrament, który jest utrwalany przy użyciu zakodowanego w formacie base64 wzmocnionego pliku GIF. Ten format jest udostępniany, gdy atrament ma być zakodowany bezpośrednio w pliku XML lub HTML w celu późniejszej konwersji na obraz. Możliwe zastosowanie tego elementu to format XML wygenerowany w celu przechowywania wszystkich informacji o tuszu i służący do generowania HTML za pomocą rozszerzalnego języka transformacji arkuszy stylów (XSLT).

Kopiuj i wklejaj pociągnięcia tuszu za pomocą schowka

W tym miejscu pokazano, jak używać schowka do przesyłania pociągnięć pióra między aplikacjami.

Aby obsługiwać funkcjonalność schowka, polecenia wycinania i kopiowania dostępne we wbudowanym kontenerze InkStrokeContainer wymagają wybrania co najmniej jednego pociągnięcia atramentu.

W tym przykładzie włączamy selekcję pociągnięcia, gdy przyciskiem pióra (lub prawym przyciskiem myszy) modyfikowane są dane wejściowe. Pełny przykład implementacji zaznaczenia pociągnięcia można znaleźć w temacie Wprowadzanie przekazywane na potrzeby zaawansowanego przetwarzania w interakcjach pióra i rysika.

Pobierz ten przykład z Zapisywanie i ładowanie pociągnięć atramentem ze schowka

  1. Najpierw skonfigurujemy interfejs użytkownika.

    Interfejs użytkownika zawiera przyciski "Cut", "Copy", "Paste" i "Clear", a także InkCanvas oraz kanwę wyboru.

<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. Następnie ustawiamy niektóre podstawowe zachowania danych wejściowych atramentu.

    InkPresenter jest skonfigurowany do interpretowania danych wejściowych zarówno z pióra, jak i myszy jako pociągnięć od atramentu (InputDeviceTypes). Odbiorniki zdarzeń kliknięcia na przyciskach, a także zdarzenia wskaźnika i pociągnięcia dla funkcji wyboru są również deklarowane tutaj.

    Pełny przykład implementacji wyboru pociągnięcia można znaleźć w temacie Wejście przekazywane do zaawansowanego przetwarzania w interakcjach pióra i rysika.

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. Na koniec, po dodaniu obsługi wyboru pociągnięcia, implementujemy funkcje schowka w obsłudze zdarzeń kliknięcia przycisków Wytnij, Kopiuj i Wklej.

    Dla operacji wycięcia najpierw wywołujemy CopySelectedToClipboard na InkStrokeContainer w InkPresenter.

    Następnie wywołujemy polecenie DeleteSelected, aby usunąć pociągnięcia z płótna pisma odręcznego.

    Na koniec usuwamy wszystkie linie zaznaczenia z kanwy zaznaczenia.

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

W celu kopiowania po prostu wywołujemy CopySelectedToClipboard na InkStrokeContainerInkPresenter.

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

W przypadku wklejania wywołujemy funkcję CanPasteFromClipboard, aby zapewnić, że zawartość schowka może być wklejona na płótno atramentu.

Jeśli tak, wywołamy funkcję PasteFromClipboard, aby wstawić pociągnięcia pisma odręcznego ze schowka do InkStrokeContainer w InkPresenterze, który następnie renderuje pociągnięcia na płótno pisma odręcznego.

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

Przykłady tematów

Inne przykłady