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


Взаимодействие с пером и Windows Ink в приложениях Windows

Изображение героя пера Surface.
Ручка Surface (доступна для покупки в Microsoft Store).

Обзор

Оптимизируйте приложение Windows для ввода пера, чтобы обеспечить как стандартные функциональные возможности устройств указателя , так и лучшие возможности Windows Ink для пользователей.

Замечание

В этом разделе рассматривается платформа Windows Ink. Общие сведения об обработке входных данных указателя (аналогично мыши, касания и сенсорной панели), см. в разделе "Обработка ввода указателя".

Использование цифровых чернил в приложении для Windows

Используйте перо Windows и возможности рукописного ввода для создания более привлекательных корпоративных приложений

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

Помимо записи базовой позиции и перемещения пера, когда пользователь пишет или рисует, ваше приложение также может отслеживать и собирать различные уровни давления, используемого на протяжении штриха. Эта информация, а также настройки формы наконечника пера, размера и угла наклона, цвета чернил и назначения (обычный ввод, стирание, выделение и выбор), позволяют предоставлять пользователям впечатления, которые очень похожи на письмо или рисование на бумаге с помощью пера, карандаша или кисти.

Замечание

Ваше приложение также может поддерживать ввод рукописного ввода с других устройств на основе указателя, включая сенсорные дигитайзеры и устройства мыши. 

Платформа цифрового чернила очень гибкая. Он предназначен для поддержки различных уровней функциональности в зависимости от ваших требований.

Рекомендации по пользовательскому интерфейсу Windows Ink см. в разделе "Элементы управления рукописным вводом".

Компоненты платформы Windows Ink

Компонент Description
InkCanvas Элемент управления платформой пользовательского интерфейса XAML, который по умолчанию получает и отображает все входные данные из пера в виде росчерка рукописного ввода или штриха удаления.
Дополнительные сведения о том, как использовать InkCanvas, см. в Распознавание росчерков Windows Ink в виде текста и хранение и получение данных росчерков Windows Ink.
InkPresenter Объект code-behind, который инстанцируется вместе с управляющим элементом InkCanvas и предоставляется через свойство InkCanvas.InkPresenter. Этот объект предоставляет все функции рукописного ввода по умолчанию, предоставляемые InkCanvas, а также комплексный набор API для дополнительной настройки и персонализации.
Дополнительные сведения о том, как использовать InkPresenter, см. в разделах Распознавание росчерков Windows Ink как текст и сохранение и восстановление данных росчерков Windows Ink.
InkToolbar Элемент управления платформы пользовательского интерфейса XAML, содержащий настраиваемую и расширяемую коллекцию кнопок, которые активируют функции, связанные с рукописным вводом, в связанном InkCanvas.
Дополнительные сведения об использовании InkToolbar см. в разделе «Добавление панели InkToolbar в приложение Windows для рукописного ввода».
IInkD2DRenderer Включает отрисовку росчерков в назначенный контекст устройства Direct2D-приложения универсального приложения для Windows вместо элемента управления по умолчанию InkCanvas. Это обеспечивает полную настройку интерфейса рукописного ввода.
Дополнительные сведения см. в примере сложного рукописного ввода.

Основы цифрового инкования на InkCanvas

Чтобы добавить основные функции рукописного ввода, просто поместите элемент управления платформы UWP InkCanvas на соответствующую страницу в приложении.

По умолчанию InkCanvas поддерживает рукописный ввод только с помощью пера. Входные данные отображаются либо в виде росчерка рукописного ввода с использованием параметров по умолчанию для цвета и толщины (черной шариковой ручки толщиной 2 пикселя), либо обрабатываются как ластик (если входные данные из наконечника ластика или кончик пера изменены кнопкой стирания).

Замечание

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

В этом примере inkCanvas накладывает фоновое изображение.

Замечание

InkCanvas имеет свойства Height и Width по умолчанию равны нулю, если он не является дочерним элементом того, который автоматически размеряет дочерние элементы, например, такие элементы управления, как StackPanel или Grid.

<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 sample"
                   Style="{ThemeResource HeaderTextBlockStyle}"
                   Margin="10,0,0,0" />            
    </StackPanel>
    <Grid Grid.Row="1">
        <Image Source="Assets\StoreLogo.png" />
        <InkCanvas x:Name="inkCanvas" />
    </Grid>
</Grid>

В этой серии изображений показано, как отрисовывается ввод пера с помощью этого элемента управления InkCanvas.

Снимок экрана: пустой объект InkCanvas с фоновым изображением. Снимок экрана InkCanvas с рукописными штрихами. Снимок экрана: InkCanvas с одним штрихом стирается.
Пустой InkCanvas с фоновым изображением. InkCanvas с чернильными штрихами. InkCanvas со стертым одним штрихом (обратите внимание, что удаление применяется ко всему штриху, а не к его части).

Функции рукописного ввода, поддерживаемые элементом управления InkCanvas , предоставляются объектом программной части под названием InkPresenter.

Для простого рукописного ввода вам не нужно беспокоиться о InkPresenter. Однако для настройки и конфигурации поведения рукописного ввода в InkCanvas необходимо получить доступ к соответствующему объекту InkPresenter.

Базовая настройка с помощью InkPresenter

Объект InkPresenter создается с каждым элементом управления InkCanvas .

Замечание

Не удается создать экземпляр InkPresenter напрямую. Вместо этого доступен через свойство InkPresenterInkCanvas

Помимо предоставления всех действий рукописного ввода по умолчанию соответствующего элемента управления InkCanvas, InkPresenter предоставляет исчерпывающий набор API для дополнительной настройки штрихов и более тонкого управления вводом пера (как стандартного, так и модифицированного). Сюда входят свойства штриха, поддерживаемые типы устройств ввода и то, обрабатываются ли входные данные объектом или передаются в приложение для обработки.

Замечание

Стандартный рукописный ввод (с кончика пера или кнопки ластика) не изменяется с использованием вторичного аппаратного средства, такого как кнопка на корпусе пера, правая кнопка мыши или аналогичное устройство.

По умолчанию поддерживается рукописный ввод только с использованием пера. Здесь мы настраиваем InkPresenter для интерпретации входных данных из пера и мыши в виде росчерков рукописного ввода. Мы также устанавливаем некоторые начальные атрибуты штрихов чернил, используемые для отрисовки на InkCanvas.

Чтобы включить ввод мыши и сенсорного ввода, задайте для свойства InputDeviceTypesinkPresenter сочетание значений CoreInputDeviceTypes , которые требуется.

public MainPage()
{
    this.InitializeComponent();

    // Set supported inking device types.
    inkCanvas.InkPresenter.InputDeviceTypes =
        Windows.UI.Core.CoreInputDeviceTypes.Mouse |
        Windows.UI.Core.CoreInputDeviceTypes.Pen;

    // Set initial ink stroke attributes.
    InkDrawingAttributes drawingAttributes = new InkDrawingAttributes();
    drawingAttributes.Color = Windows.UI.Colors.Black;
    drawingAttributes.IgnorePressure = false;
    drawingAttributes.FitToCurve = true;
    inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(drawingAttributes);
}

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

Здесь мы позволим пользователю выбрать из списка цветов рукописного ввода.

<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 customization sample"
                   VerticalAlignment="Center"
                   Style="{ThemeResource HeaderTextBlockStyle}"
                   Margin="10,0,0,0" />
        <TextBlock Text="Color:"
                   Style="{StaticResource SubheaderTextBlockStyle}"
                   VerticalAlignment="Center"
                   Margin="50,0,10,0"/>
        <ComboBox x:Name="PenColor"
                  VerticalAlignment="Center"
                  SelectedIndex="0"
                  SelectionChanged="OnPenColorChanged">
            <ComboBoxItem Content="Black"/>
            <ComboBoxItem Content="Red"/>
        </ComboBox>
    </StackPanel>
    <Grid Grid.Row="1">
        <Image Source="Assets\StoreLogo.png" />
        <InkCanvas x:Name="inkCanvas" />
    </Grid>
</Grid>

Затем мы обрабатываем изменения выбранного цвета и соответствующим образом обновляем атрибуты штриха.

// Update ink stroke color for new strokes.
private void OnPenColorChanged(object sender, SelectionChangedEventArgs e)
{
    if (inkCanvas != null)
    {
        InkDrawingAttributes drawingAttributes =
            inkCanvas.InkPresenter.CopyDefaultDrawingAttributes();

        string value = ((ComboBoxItem)PenColor.SelectedItem).Content.ToString();

        switch (value)
        {
            case "Black":
                drawingAttributes.Color = Windows.UI.Colors.Black;
                break;
            case "Red":
                drawingAttributes.Color = Windows.UI.Colors.Red;
                break;
            default:
                drawingAttributes.Color = Windows.UI.Colors.Black;
                break;
        };

        inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(drawingAttributes);
    }
}

На этих изображениях показано, как настраиваются и обрабатываются данные с пера с помощью InkPresenter.

Скриншот, показывающий InkCanvas с черными росчерками по умолчанию.

InkCanvas с черными рукописными росчерками по умолчанию.

Снимок экрана: InkCanvas с выбранными пользователем красными мазками.

InkCanvas на котором пользователь выбрал красные штрихи.

Чтобы обеспечить функциональность, выходящую за рамки рукописного ввода и стирания, например, выбор штриха, ваше приложение должно учитывать определенные входные данные для InkPresenter, чтобы передавать их необработанными для обработки непосредственно вашим приложением.

Сквозной ввод для расширенной обработки

По умолчанию InkPresenter обрабатывает все вводимые данные как рукописный штрих или стирающий штрих, включая данные, изменённые дополнительным аппаратным средством, например кнопкой на корпусе пера, правой кнопкой мыши или аналогичной. Однако обычно пользователи ожидают некоторые дополнительные функциональные возможности или измененное поведение с этими дополнительными разрешениями.

В некоторых случаях также может потребоваться предоставить дополнительные функции для перьев без дополнительных возможностей (функции, которые обычно не связаны с наконечником пера), для других типов входных устройств или измененного поведения на основе выбора пользователя в пользовательском интерфейсе вашего приложения.

Для поддержки этого можно настроить InkPresenter , чтобы оставить определенные входные данные без обработки. Затем эти необработанные входные данные передаются в приложение для обработки.

Пример – Использование необработанных входных данных для реализации выбора штрихов

Платформа Windows Ink не предоставляет встроенную поддержку действий, требующих измененных входных данных, таких как выбор штриха. Для поддержки таких функций необходимо предоставить пользовательское решение в приложениях.

В следующем примере кода (весь код находится в файлах MainPage.xaml и MainPage.xaml.cs) показано, как включить выделение штрихов при изменении входных данных с помощью кнопки "корпуса ручки" (или правой кнопки мыши).

  1. Сначала мы настраиваем пользовательский интерфейс в MainPage.xaml.

    Здесь мы добавим холст (под InkCanvas), чтобы нарисовать штрих выделения. Использование отдельного слоя для рисования штриха выделения позволяет не изменять InkCanvas и его содержимое.

    Снимок экрана: пустой объект 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="Advanced ink customization sample"
            VerticalAlignment="Center"
            Style="{ThemeResource HeaderTextBlockStyle}"
            Margin="10,0,0,0" />
        </StackPanel>
        <Grid Grid.Row="1">
          <!-- Canvas for displaying selection UI. -->
          <Canvas x:Name="selectionCanvas"/>
          <!-- Inking area -->
          <InkCanvas x:Name="inkCanvas"/>
        </Grid>
      </Grid>
    
  2. В MainPage.xaml.cs мы объявляем несколько глобальных переменных для хранения ссылок на аспекты пользовательского интерфейса выбора. В частности, штрих выделения лассо и рамка, которая выделяет выбранные штрихи.

      // Stroke selection tool.
      private Polyline lasso;
      // Stroke selection area.
      private Rect boundingRect;
    
  3. Затем мы настроим InkPresenter для интерпретации входных данных с пера и мыши как рукописные штрихи и задаем начальные атрибуты штрихов, используемые для их визуализации на InkCanvas.

    Самое главное, мы используем свойство InputProcessingConfigurationinkPresenter , чтобы указать, что любые измененные входные данные должны обрабатываться приложением. Измененные входные данные задаются путем назначения InputProcessingConfiguration.RightDragAction значения InkInputRightDragAction.LeaveUnprocessed. Если это значение установлено, InkPresenter передает через класс InkUnprocessedInput набор событий указателя для вашей обработки.

    Мы назначаем слушателей для необработанных событий PointerPressed, PointerMoved и PointerReleased, передаваемых InkPresenter. Все функции выбора реализованы в обработчиках этих событий.

    Наконец, мы назначаем прослушиватели для событий StrokeStarted и StrokesErased элемента InkPresenter. Обработчики этих событий используются для очистки пользовательского интерфейса выбора при запуске нового штриха или удалении существующего штриха.

    Снимок экрана примера приложения Advance ink customization, показывающий холст для рукописного ввода с черными росчерками по умолчанию.

      public MainPage()
      {
        this.InitializeComponent();
    
        // Set supported inking device types.
        inkCanvas.InkPresenter.InputDeviceTypes =
          Windows.UI.Core.CoreInputDeviceTypes.Mouse |
          Windows.UI.Core.CoreInputDeviceTypes.Pen;
    
        // Set initial ink stroke attributes.
        InkDrawingAttributes drawingAttributes = new InkDrawingAttributes();
        drawingAttributes.Color = Windows.UI.Colors.Black;
        drawingAttributes.IgnorePressure = false;
        drawingAttributes.FitToCurve = true;
        inkCanvas.InkPresenter.UpdateDefaultDrawingAttributes(drawingAttributes);
    
        // 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;
      }
    
  4. Затем мы определяем обработчики для необработанных событий PointerPressed, PointerMoved и PointerReleased, передаваемых через InkPresenter.

    Все функции выбора реализованы в этих обработчиках, включая штрих лассо и ограничивающий прямоугольник.

    Снимок экрана инструмента выделения лассо.

      // Handle unprocessed pointer events from modified input.
      // The input is used to provide selection functionality.
      // Selection UI is drawn on a canvas under the InkCanvas.
      private void UnprocessedInput_PointerPressed(
        InkUnprocessedInput sender, PointerEventArgs args)
      {
        // Initialize a selection lasso.
        lasso = new Polyline()
        {
            Stroke = new SolidColorBrush(Windows.UI.Colors.Blue),
            StrokeThickness = 1,
            StrokeDashArray = new DoubleCollection() { 5, 2 },
            };
    
            lasso.Points.Add(args.CurrentPoint.RawPosition);
    
            selectionCanvas.Children.Add(lasso);
        }
    
        private void UnprocessedInput_PointerMoved(
          InkUnprocessedInput sender, PointerEventArgs args)
        {
          // Add a point to the lasso Polyline object.
          lasso.Points.Add(args.CurrentPoint.RawPosition);
        }
    
        private void UnprocessedInput_PointerReleased(
          InkUnprocessedInput sender, PointerEventArgs args)
        {
          // Add the final point to the Polyline object and
          // select strokes within the lasso area.
          // Draw a bounding box on the selection canvas
          // around the selected ink strokes.
          lasso.Points.Add(args.CurrentPoint.RawPosition);
    
          boundingRect =
            inkCanvas.InkPresenter.StrokeContainer.SelectWithPolyLine(
              lasso.Points);
    
          DrawBoundingRect();
        }
    
  5. Чтобы завершить обработчик событий PointerReleased, мы очищаем слой выбора всего содержимого (штрих лассо), а затем рисуем один ограничивающий прямоугольник вокруг росчерков рукописного ввода, охватываемых областью лассо.

    Снимок экрана: ограничивающий прямоугольник выделения.

      // Draw a bounding rectangle, on the selection canvas, encompassing
      // all ink strokes within the lasso area.
      private void DrawBoundingRect()
      {
        // Clear all existing content from the selection canvas.
        selectionCanvas.Children.Clear();
    
        // Draw a bounding rectangle only if there are ink strokes
        // within the lasso area.
        if (!((boundingRect.Width == 0) ||
          (boundingRect.Height == 0) ||
          boundingRect.IsEmpty))
          {
            var rectangle = new Rectangle()
            {
              Stroke = new SolidColorBrush(Windows.UI.Colors.Blue),
                StrokeThickness = 1,
                StrokeDashArray = new DoubleCollection() { 5, 2 },
                Width = boundingRect.Width,
                Height = boundingRect.Height
            };
    
            Canvas.SetLeft(rectangle, boundingRect.X);
            Canvas.SetTop(rectangle, boundingRect.Y);
    
            selectionCanvas.Children.Add(rectangle);
          }
        }
    
  6. Наконец, мы определим обработчики для событий StrokeStarted иStrokesErased InkPresenter.

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

      // Handle new ink or erase strokes to clean up selection UI.
      private void StrokeInput_StrokeStarted(
        InkStrokeInput sender, Windows.UI.Core.PointerEventArgs args)
      {
        ClearSelection();
      }
    
      private void InkPresenter_StrokesErased(
        InkPresenter sender, InkStrokesErasedEventArgs args)
      {
        ClearSelection();
      }
    
  7. Вот функция, которая удаляет весь интерфейс выбора с холста выбора, когда начинается новый штрих или удаляется существующий штрих.

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

Настраиваемая отрисовка чернил

По умолчанию входные данные рукописного ввода обрабатываются в фоновом потоке с низкой задержкой и отображаются во время выполнения или "мокрые", как оно нарисовано. После завершения росчерка (перо или палец оторваны, или кнопка мыши отпущена), штрих обрабатывается в потоке пользовательского интерфейса и отрисовывается "сухим" на слое InkCanvas (над содержимым приложения и заменяя мокрые чернила).

Это поведение по умолчанию можно переопределить и полностью контролировать процесс ввода данных пером путем "настраиваемой сушки" мокрых штрихов. Хотя поведение по умолчанию обычно достаточно для большинства приложений, существует несколько случаев, когда может потребоваться настраиваемая сушка, к ним относятся:

  • Более эффективное управление большими или сложными коллекциями рукописных росчерков
  • Более эффективная поддержка перемещения и масштабирования на больших холстах для рукописного ввода
  • Чередование чернил и других объектов, таких как фигуры или текст, при сохранении порядка слоев (z-order)
  • Высушивание и преобразование чернил синхронно в фигуру DirectX (например, прямая линия или другая фигура растеризуются и интегрируются в содержимое приложения в отличие от отдельного слоя InkCanvas).

Для пользовательской сушки требуется объект IInkD2DRenderer для управления вводом рукописного ввода и отрисовки его в контексте устройства Direct2D универсального приложения Для Windows вместо элемента управления InkCanvas по умолчанию.

При вызове ActivateCustomDrying (до загрузки InkCanvas), приложение создает объект InkSynchronizer для настройки процесса прорисовки штрихов рукописного ввода в SurfaceImageSource или VirtualSurfaceImageSource.

SurfaceImageSource и VirtualSurfaceImageSource предоставляют общую поверхность DirectX, на которой ваше приложение может рисовать и интегрировать её в содержимое приложения, хотя VSIS предоставляет виртуальную поверхность, которая больше экрана для более эффективного панорамирования и масштабирования. Поскольку визуальные обновления этих поверхностей синхронизированы с потоком пользовательского интерфейса XAML, когда чернила наносятся на любую из этих поверхностей, можно одновременно удалить нарисованный рукописный ввод из InkCanvas.

Вы также можете настроить сухой ввод чернил для SwapChainPanel, но синхронизация с потоком пользовательского интерфейса не гарантируется, и может возникнуть задержка между моментом отрисовки чернил на SwapChainPanel и удалением чернил с InkCanvas.

Для полного примера этой функциональности см. сложный образец чернил.

Замечание

Настраиваемая сушка и панель инструментов для работы с чернилами
Если приложение переопределяет поведение отрисовки рукописного ввода по умолчанию для InkPresenter с помощью пользовательской реализации сушки, отрисованные росчерки рукописного ввода больше не доступны для панели InkToolbar, а встроенные команды удаления на панели InkToolbar не работают должным образом. Чтобы обеспечить функциональность стирания, необходимо обрабатывать все события указателя, выполнять тестирование попаданий на каждом штрихе и переопределять встроенную команду "Стереть все рукописные вводы".

Тема Description
Распознавание росчерков Преобразование рукописных росчерков в текст с помощью распознавания почерка или в фигуры с использованием настраиваемого распознавания.
Хранение и извлечение штрихов рукописного ввода Храните данные о штрихах рукописного ввода в файле формата GIF, используя встроенные метаданные в формате сериализованного рукописного ввода (ISF).
Добавьте "InkToolbar" в приложение рукописного ввода Windows Добавьте панель InkToolbar по умолчанию в приложение рукописного ввода Windows, добавьте настраиваемую кнопку пера в InkToolbar и привяжите её к пользовательскому определению пера.

Программные интерфейсы

Samples

Архивные примеры