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

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

Общие сведения

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

Примечание

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

Использование рукописного ввода в приложении для Windows

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

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

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

Примечание

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

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

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

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

Компонент Описание
InkCanvas Элемент управления платформы пользовательского интерфейса XAML, который по умолчанию получает и отображает все входные данные от пера как росчерк пера или росчерк стирания.
Дополнительные сведения о том, как использовать InkCanvas, см. в разделах Распознавание росчерков пера Windows Ink как текста и Хранение и извлечение данных движения пера Windows Ink.
InkPresenter Объект кода программной части, создаваемый вместе с элементом управления 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 нельзя создавать напрямую. Вместо этого доступ к нему осуществляется с помощью свойства InkPresenter элемента InkCanvas

Помимо всех реакций на рукописный ввод по умолчанию для соответствующего элемента управления 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.

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

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

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

    Снимок экрана: пример приложения для настройки рукописного ввода в Advances, в котором показаны рукописные фрагменты с черными росчерками чернил по умолчанию.

      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. Наконец мы определяем обработчики для событий InkPresenter StrokeStarted и StrokesErased.

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

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

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

Вызывая ActivateCustomDrying (до загрузки элемента InkCanvas), приложение создает объект InkSynchronizer, чтобы настроить отображение росчерка пера сухим в SurfaceImageSource или VirtualSurfaceImageSource.

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

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

Полный пример этой функциональности см. в статье Сложный пример рукописного ввода.

Примечание

Пользовательская сушка и InkToolbar
Если ваше приложение переопределяет поведение по умолчанию для отрисовки рукописных данных объекта InkPresenter пользовательской реализацией сушки, отрисованные росчерки пера более недоступны для InkToolbar, а встроенные команды стирания InkToolbar не работают должным образом. Для реализации функции стирания необходимо обрабатывать все события указателя, выполнять проверку нажатия для каждого штриха и переопределить встроенную команду «Удалить все рукописные данные».

Раздел Описание
Распознавание росчерков пера Преобразуйте росчерки пера в текст с помощью распознавания рукописного ввода или в фигуры с помощью настраиваемого распознавания.
Хранение и извлечение росчерков пера Храните данные росчерка пера в GIF-файле, используя встроенные метаданные формата Ink Serialized Format (ISF).
Добавление inkToolbar в приложение windows для рукописного ввода Добавьте inkToolbar по умолчанию в приложение для рукописного ввода в приложении Windows, добавьте настраиваемую кнопку пера в inkToolbar и привяжите кнопку настраиваемого пера к пользовательскому определению пера.

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

Примеры

Примеры архивов