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


Хранить и восстанавливать данные росчерков Windows Ink.

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

Замечание

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

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

Важные API: InkCanvas, Windows.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. Наконец, мы загружаем чернила в обработчик событий нажатия кнопки Load.

    Объект FileOpenPicker позволяет пользователю выбрать файл и расположение, откуда следует извлечь сохраненные данные рукописного ввода.

    После выбора файла мы открываем поток IRandomAccessStream, установленный для чтения.

    Затем мы вызываем 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. Это самое компактное устойчивое представление цифровых чернил. Его можно внедрить в двоичный формат документа или поместить непосредственно в буфер обмена.
Base64InkSerializedFormat Указывает чернила, которые сохраняются посредством кодирования ISF в поток base64. Этот формат предоставляется для кодирования чернил непосредственно в XML-файле или HTML-файле.
Гиф Указывает чернила, которые сохраняются с использованием 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 на InkStrokeContainerInkPresenter.

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

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

В этом случае мы вызываем PasteFromClipboard для вставки росчерков рукописного ввода буфера обмена в InkStrokeContainerInkPresenter, который затем отрисовывает росчерки на холсте рукописного ввода.

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

Примеры темы

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