Перехват ввода, осуществляемого пером

В архитектуре System.Windows.Input.StylusPlugIns реализован механизм реализации низкоуровневого управления входными данными Stylus и создания объектов Stroke цифрового рукописного ввода. Класс StylusPlugIn предоставляет механизм реализации настраиваемого поведения и его применения к потоку данных, поступающих от устройства пера, для обеспечения оптимальной производительности.

В этом разделе содержатся следующие подразделы:

Архитектура

StylusPlugIn представляет собой эволюцию API-интерфейсов StylusInput, описанных в разделе Доступ к вводу с помощью пера и управление им.

Каждый UIElement имеет свойство StylusPlugIns, представляющее собой StylusPlugInCollection. StylusPlugIn можно добавить к свойству StylusPlugIns элемента для управления данными StylusPoint по мере их создания. Данные StylusPoint включают в себя все свойства, поддерживаемые системным диджитайзером, в том числе, данные точек X и Y, а также данные PressureFactor.

Объекты StylusPlugIn вставляются непосредственно в поток данных, поступающих из устройства Stylus, при добавлении StylusPlugIn в свойство StylusPlugIns. Порядок добавления подключаемых модулей StylusPlugIns в коллекцию определяет порядок, в котором они получают данные StylusPoint. Например, если добавить подключаемый модуль фильтра, ограничивающий входные данные в определенном регионе, а затем добавить подключаемый модуль, распознающий жесты по мере их записи, то последний подключаемый модуль будет получать отфильтрованные данные StylusPoint.

Реализация подключаемых модулей пера

Чтобы реализовать подключаемый модуль, создайте класс, производный от StylusPlugIn. Этот класс применяется к потоку данных в ходе их поступления из Stylus. В этом классе можно изменять значения данных StylusPoint.

Внимание

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

В следующем примере показан подключаемый модуль, ограничивающий ввод с помощью пера путем изменения значений X и Y в данных StylusPoint по мере их поступления из устройства Stylus.

using System;
using System.Windows.Media;
using System.Windows;
using System.Windows.Input.StylusPlugIns;
using System.Windows.Input;
using System.Windows.Ink;
Imports System.Windows.Media
Imports System.Windows
Imports System.Windows.Input.StylusPlugIns
Imports System.Windows.Input
Imports System.Windows.Ink
// A StylusPlugin that restricts the input area.
class FilterPlugin : StylusPlugIn
{
    protected override void OnStylusDown(RawStylusInput rawStylusInput)
    {
        // Call the base class before modifying the data.
        base.OnStylusDown(rawStylusInput);

        // Restrict the stylus input.
        Filter(rawStylusInput);
    }

    protected override void OnStylusMove(RawStylusInput rawStylusInput)
    {
        // Call the base class before modifying the data.
        base.OnStylusMove(rawStylusInput);

        // Restrict the stylus input.
        Filter(rawStylusInput);
    }

    protected override void OnStylusUp(RawStylusInput rawStylusInput)
    {
        // Call the base class before modifying the data.
        base.OnStylusUp(rawStylusInput);

        // Restrict the stylus input
        Filter(rawStylusInput);
    }

    private void Filter(RawStylusInput rawStylusInput)
    {
        // Get the StylusPoints that have come in.
        StylusPointCollection stylusPoints = rawStylusInput.GetStylusPoints();

        // Modify the (X,Y) data to move the points
        // inside the acceptable input area, if necessary.
        for (int i = 0; i < stylusPoints.Count; i++)
        {
            StylusPoint sp = stylusPoints[i];
            if (sp.X < 50) sp.X = 50;
            if (sp.X > 250) sp.X = 250;
            if (sp.Y < 50) sp.Y = 50;
            if (sp.Y > 250) sp.Y = 250;
            stylusPoints[i] = sp;
        }

        // Copy the modified StylusPoints back to the RawStylusInput.
        rawStylusInput.SetStylusPoints(stylusPoints);
    }
}
' A StylusPlugin that restricts the input area.
Class FilterPlugin
    Inherits StylusPlugIn

    Protected Overrides Sub OnStylusDown(ByVal rawStylusInput As RawStylusInput)
        ' Call the base class before modifying the data.
        MyBase.OnStylusDown(rawStylusInput)

        ' Restrict the stylus input.
        Filter(rawStylusInput)

    End Sub


    Protected Overrides Sub OnStylusMove(ByVal rawStylusInput As RawStylusInput)
        ' Call the base class before modifying the data.
        MyBase.OnStylusMove(rawStylusInput)

        ' Restrict the stylus input.
        Filter(rawStylusInput)

    End Sub


    Protected Overrides Sub OnStylusUp(ByVal rawStylusInput As RawStylusInput)
        ' Call the base class before modifying the data.
        MyBase.OnStylusUp(rawStylusInput)

        ' Restrict the stylus input
        Filter(rawStylusInput)

    End Sub


    Private Sub Filter(ByVal rawStylusInput As RawStylusInput)
        ' Get the StylusPoints that have come in.
        Dim stylusPoints As StylusPointCollection = rawStylusInput.GetStylusPoints()

        ' Modify the (X,Y) data to move the points 
        ' inside the acceptable input area, if necessary.
        Dim i As Integer
        For i = 0 To stylusPoints.Count - 1
            Dim sp As StylusPoint = stylusPoints(i)
            If sp.X < 50 Then
                sp.X = 50
            End If
            If sp.X > 250 Then
                sp.X = 250
            End If
            If sp.Y < 50 Then
                sp.Y = 50
            End If
            If sp.Y > 250 Then
                sp.Y = 250
            End If
            stylusPoints(i) = sp
        Next i

        ' Copy the modified StylusPoints back to the RawStylusInput.
        rawStylusInput.SetStylusPoints(stylusPoints)

    End Sub
End Class

Добавление подключаемого модуля в InkCanvas

Самый простой способ использовать пользовательский подключаемый модуль — реализовать класс, производный от InkCanvas, и добавить его в свойство StylusPlugIns.

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

public class FilterInkCanvas : InkCanvas
{
    FilterPlugin filter = new FilterPlugin();

    public FilterInkCanvas()
        : base()
    {
        this.StylusPlugIns.Add(filter);
    }
}

Если добавить FilterInkCanvas в приложение и запустить его, вы заметите, что рукописный ввод не будет ограничен регионом до тех пор, пока пользователь не выполнит мазок кистью. Это связано с тем, что InkCanvas содержит свойство DynamicRenderer, которое является StylusPlugIn и уже является элементом коллекции StylusPlugIns. Пользовательский StylusPlugIn, добавленный в коллекцию StylusPlugIns, получает данные StylusPoint после того, как DynamicRenderer получит данные. В результате данные StylusPoint не будут фильтроваться до тех пор, пока пользователь не поднимет перо, чтобы закончить мазок. Для фильтрации рукописного ввода в ходе его отрисовки пользователем FilterPlugin необходимо вставить перед DynamicRenderer.

В следующем коде C# показан пользовательский InkCanvas, который фильтрует рукописный ввод по мере его отрисовки.

public class DynamicallyFilteredInkCanvas : InkCanvas
{
    FilterPlugin filter = new FilterPlugin();

    public DynamicallyFilteredInkCanvas()
        : base()
    {
        int dynamicRenderIndex =
            this.StylusPlugIns.IndexOf(this.DynamicRenderer);

        this.StylusPlugIns.Insert(dynamicRenderIndex, filter);
    }
}

Заключение

Благодаря созданию собственных производных классов StylusPlugIn и их вставке в коллекции StylusPlugInCollection можно существенно улучшить поведение рукописного ввода. У вас есть доступ к данным StylusPoint по мере их создания, что позволяет настраивать входные данные Stylus. Так как у вас есть такой низкоуровневый доступ к данным StylusPoint, вы можете реализовать коллекцию рукописных фрагментов и обеспечивать отрисовку с оптимальной производительностью для приложения.

См. также