Поделиться через


Хранение и извлечение данных движения пера Windows Ink

Приложения Windows, поддерживающие Windows Ink, могут сериализовать и десериализировать росчерки рукописного ввода в файл сериализованного формата рукописного ввода (ISF). ФАЙЛ ISF — это GIF-изображение с дополнительными метаданными для всех свойств и поведения росчерка рукописного ввода. Приложения, которые не включены в рукописный ввод, могут просматривать статический GIF-образ, включая прозрачность фона альфа-канала.

Примечание.

ISF — это самое компактное постоянное представление рукописного ввода. Он может быть внедрен в двоичный формат документа, например GIF-файл или помещен непосредственно в буфер обмена.

Спецификация сериализованного формата рукописного ввода (ISF) можно скачать из Центра загрузки Майкрософт.

Важные API: InkCanvas, Windows.UI.Input.Inking

Сохранение росчерков рукописного ввода в файл

Здесь мы покажем, как сохранить росчерки рукописного ввода, нарисованные на элементе управления InkCanvas.

Скачайте этот пример из файла сохранения и загрузки росчерков рукописного ввода из файла сериализованного формата рукописного ввода (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 .

Скачайте этот пример из файла сохранения и загрузки росчерков рукописного ввода из файла сериализованного формата рукописного ввода (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 позволяет пользователю выбрать как файл, так и расположение, откуда получить сохраненные данные рукописного ввода.

    После выбора файла мы открываем поток 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 поддерживает следующие форматы для обратной совместимости.

Формат Description
InkSerializedFormat Указывает рукописный ввод, сохраняемый с помощью ISF. Это самое компактное постоянное представление рукописного ввода. Его можно внедрить в двоичный формат документа или поместить непосредственно в буфер обмена.
Base64InkSerializedFormat Указывает рукописный ввод, который сохраняется путем кодирования ISF в виде потока base64. Этот формат предоставляется таким образом, чтобы рукописный ввод можно кодировать непосредственно в XML-файле или HTML-файле.
Gif Указывает рукописный ввод, который сохраняется с помощью GIF-файла, содержащего ISF в качестве метаданных, внедренных в файл. Это позволяет просматривать рукописные данные в приложениях, которые не включены рукописным вводом и поддерживают полную точность рукописного ввода при возвращении в приложение с поддержкой рукописного ввода. Этот формат идеально подходит при транспортировке рукописного содержимого в HTML-файле и для использования рукописными и не рукописными приложениями.
Base64Gif Указывает рукописный ввод, который сохраняется с помощью защищенного GIF в кодировке Base64. Этот формат предоставляется при кодировании рукописного ввода непосредственно в XML-файле или HTML-файле для последующего преобразования в изображение. Это можно использовать в формате XML, созданном для хранения всех данных рукописного ввода и используемых для создания HTML через расширяемые преобразования языка стилей (XSLT).

Копирование и вставка росчерков рукописного ввода с помощью буфера обмена

Здесь мы покажем, как использовать буфер обмена для передачи росчерков рукописного ввода между приложениями.

Для поддержки функций буфера обмена встроенные команды выреза и копирования 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. Наконец, после добавления поддержки выделения штрихов мы реализуем функциональные возможности буфера обмена в обработчиках событий щелчка кнопок "Вырезать", "Копировать" и "Вставить ".

    Для вырезания сначала вызывается 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;
        }
    }

Для копирования мы просто вызываем CopySelectedToClipboard в InkStrokeContainer inkPresenter.

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

Для вставки вызовите CanPasteFromClipboard , чтобы убедиться, что содержимое в буфере обмена можно вставлять на холст рукописного ввода.

В этом случае мы вызываем InsertFromClipboard, чтобы вставить росчерки рукописного ввода буфера обмена в 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.
        }
    }

Примеры раздела

Другие примеры