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


Пример подключаемого модуля RealTimeStylus

Это приложение демонстрирует работу с классом RealTimeStylus. Подробный обзор API StylusInput, включая класс RealTimeStylus, см. в разделе Доступ и управление вводом пера. См. сведения о синхронных и асинхронных подключаемых модулях в разделе Подключаемые модули и класс RealTimeStylus.

Обзор примера

Подключаемые модули, объекты, реализующие интерфейс IStylusSyncPlugin или IStylusAsyncPlugin, можно добавить в объект RealTimeStylus. В этом примере приложения используется несколько типов подключаемых модулей:

  • Плагин фильтра пакетов: изменяет пакеты. Подключаемый модуль фильтра пакетов в этом примере изменяет сведения о пакете путем ограничения всех данных пакета (x,y) в прямоугольной области.
  • Подключаемый модуль настраиваемого динамического рендеринга: изменяет свойства динамической отрисовки. Пользовательский подключаемый модуль динамической отрисовки в этом примере изменяет способ отображения чернил путем рисования небольшого круга вокруг каждой точки (x,y) штриха.
  • Подключаемый модуль динамического отрисовщика: изменяет качества динамической отрисовки. В этом примере показано использование объекта DynamicRendererв качестве подключаемого модуля для обработки динамического отображения рукописного ввода.
  • Подключаемый модуль распознавателя жестов: распознает жесты приложения. В этом примере показано использование объекта GestureRecognizer в качестве плагина для распознавания жестов приложения (когда он работает на системе, в которой присутствует распознаватель жестов от Майкрософт).

Кроме того, этот пример предоставляет пользовательский интерфейс, позволяющий пользователю добавлять, удалять и изменять порядок каждого подключаемого модуля в коллекции. Пример решения содержит два проекта: RealTimeStylusPluginApp и RealTimeStylusPlugins. RealTimeStylusPluginApp содержит пользовательский интерфейс для примера. RealTimeStylusPlugins содержит реализации подключаемых модулей. Проект RealTimeStylusPlugins определяет пространство имен RealTimeStylusPlugins, содержащее фильтр пакетов и подключаемые модули динамического отрисовщика. Это пространство имен ссылается на проект RealTimeStylusPluginApp. Проект RealTimeStylusPlugins использует Microsoft.Ink, Microsoft.StylusInputи пространства имён Microsoft.StylusInput.PluginData.

Смотрите обзор пространств имен Microsoft.StylusInput и Microsoft.StylusInput.PluginData в архитектуре API StylusInput.

Плагин фильтра пакетов

Подключаемый модуль фильтрации пакетов является синхронным плагином, демонстрирующим модификацию пакета. В частности, он определяет прямоугольник на форме. Все пакеты, нарисованные за пределами области, отображаются внутри области. Класс подключаемого модуля PacketFilterPluginрегистрируется для уведомления о событиях ввода StylusDown, StylusUpи Packets пером. Класс реализует методы StylusDown, StylusUpи методы Packets, определенные в классе IStylusSyncPlugin.

Общедоступному конструктору для PacketFilterPlugin требуется структура прямоугольника . Этот прямоугольник определяет прямоугольную область в координатах пространства рукописного ввода (01mm = 1 единица HIMETRIC), в которой будут содержаться пакеты. Прямоугольник хранится в частном поле rectangle.

public class PacketFilterPlugin:IStylusSyncPlugin  
{
    private System.Drawing.Rectangle rectangle = System.Drawing.Rectangle.Empty;
    public PacketFilterPlugin(Rectangle r)
    {
        rectangle = r;
    }
    // ...

Класс PacketFilterPlugin регистрирует уведомления о событиях путем реализации аксессора get для свойства DataInterest. В этом случае плагин заинтересован в реагировании на уведомления StylusDown, Packets, StylusUpи Error. Пример возвращает эти значения, как определено в перечислении DataInterestMask. Метод StylusDown вызывается, когда кончик пера касается поверхности дигитайзера. Метод StylusUp вызывается, когда кончик пера покидает поверхность дигитайзера. Метод пакетов вызывается, когда объект RealTimeStylus получает пакеты. Метод ошибки вызывается, когда текущий плагин или предыдущий плагин вызывает исключение.

public DataInterestMask DataInterest
{
    get
    {
        return DataInterestMask.StylusDown | 
               DataInterestMask.Packets | 
               DataInterestMask.StylusUp | 
               DataInterestMask.Error;
    }
}           
    //...

Класс PacketFilterPlugin обрабатывает большинство этих уведомлений в вспомогательном методе ModifyPacketData. Метод ModifyPacketData получает значения x и y для каждого нового пакета из класса PacketsData. Если любое значение находится за пределами прямоугольника, метод заменяет значение ближайшей точкой, которая по-прежнему находится в прямоугольнике. Это пример того, как плагин может заменить данные пакетов в процессе их получения из потока ввода пера.

private void ModifyPacketData(StylusDataBase data)
{
    for (int i = 0; i < data.Count ; i += data.PacketPropertyCount)
    {
        // packet data always has x followed by y followed by the rest
        int x = data[i];
        int y = data[i+1];

        // Constrain points to the input rectangle
        x = Math.Max(x, rectangle.Left);
        x = Math.Min(x, rectangle.Right);
        y = Math.Max(y, rectangle.Top);
        y = Math.Min(y, rectangle.Bottom);

        // If necessary, modify the x,y packet data
        if (x != data[i])
        {
            data[i] = x;
        }
        if (y != data[i+1])
        {
            data[i+1] = y;
        } 
    }
}

Подключаемый модуль пользовательского динамического рендера

Класс CustomDynamicRenderer также реализует класс IStylusSyncPlugin для получения уведомлений с вводом пера. Затем он обрабатывает уведомление Packets, чтобы нарисовать небольшой круг вокруг каждой новой точки пакета.

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

private Graphics myGraphics;

public CustomDynamicRendererPlugin(Graphics g)
{
    myGraphics = g;
}
        //...
            

Когда подключаемый модуль динамического отрисовщика получает уведомление о пакетах, он извлекает данные (x,y) и рисует небольшой зеленый круг вокруг точки. Это пример кастомной отрисовки на основе потока ввода пером.

public void Packets(RealTimeStylus sender,  PacketsData data)
{           
    for (int i = 0; i < data.Count; i += data.PacketPropertyCount)
    {
        // Packet data always has x followed by y followed by the rest
        Point point = new Point(data[i], data[i+1]);

        // Since the packet data is in Ink Space coordinates, we need to convert to Pixels...
        point.X = (int)Math.Round((float)point.X * (float)myGraphics.DpiX/2540.0F);
        point.Y = (int)Math.Round((float)point.Y * (float)myGraphics.DpiY/2540.0F);

        // Draw a circle corresponding to the packet
        myGraphics.DrawEllipse(Pens.Green, point.X - 2, point.Y - 2, 4, 4);
    }
}

Проект RealTimeStylusPluginApp

Проект RealTimeStylusPluginApp демонстрирует ранее описанные подключаемые модули, а также подключаемые модули GestureRecognizer и DynamicRenderer. Пользовательский интерфейс проекта состоит из:

  • Форма, содержащая элемент управления GroupBox, используемый для определения области рукописного ввода.
  • Элемент управления CheckedListBox для списка и выбора доступных подключаемых модулей.
  • Пара объектов Button для активации повторного упорядочивания плагинов.

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

Сам класс RealTimeStylusPluginApp реализует класс IStylusAsyncPlugin. Это необходимо, чтобы класс RealTimeStylusPluginApp мог быть уведомлён, когда подключаемый модуль GestureRecognizer добавляет данные жестов в выходную очередь. Приложение регистрируется для уведомления о CustomStylusDataAdded. При получении данных жеста RealTimeStylusPluginApp помещает его описание в строку состояния в нижней части формы.

public void CustomStylusDataAdded(RealTimeStylus sender, CustomStylusData data)
{
    if (data.CustomDataId == GestureRecognizer.GestureRecognitionDataGuid)
    {
        GestureRecognitionData grd = data.Data as GestureRecognitionData;
        if (grd != null)
        {
            if (grd.Count > 0)
            {
                GestureAlternate ga = grd[0];
                sbGesture.Text = "Gesture=" + ga.Id + ", Confidence=" + ga.Confidence;
            }
        }
    }
}

Заметка

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

 

В обработчике событий Load Form приложение создает экземпляры классов PacketFilter и CustomDynamicRenderer и добавляет их в поле списка. Затем приложение пытается создать экземпляр класса GestureRecognizer, а при успешном выполнении добавляет его в поле списка. Это завершается ошибкой, если распознаватель жестов отсутствует в системе. Затем приложение создает экземпляр объекта DynamicRenderer и добавляет его в поле списка. Наконец, приложение активирует каждый подключаемый модуль и сам объект RealTimeStylus.

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

private void RemoveFromPluginCollection(int index)
{
    IStylusSyncPlugin plugin = ((PluginListItem)chklbPlugins.Items[index]).Plugin;

    bool rtsEnabled = myRealTimeStylus.Enabled;
    myRealTimeStylus.Enabled = false;
    myRealTimeStylus.SyncPluginCollection.Remove(plugin);
    myRealTimeStylus.Enabled = rtsEnabled;
}

Microsoft.StylusInput.DynamicRenderer

Microsoft.StylusInput.GestureRecognizer

Microsoft.StylusInput.RealTimeStylus

Microsoft.StylusInput.DataInterestMask

Microsoft.StylusInput.IStylusSyncPlugin

Microsoft.StylusInput.IStylusAsyncPlugin

Microsoft.StylusInput.PluginData.PacketsData

Доступ и обработка ввода с пера

Плагины и класс RealTimeStylus

Пример коллекции рукописного ввода RealTimeStylus