Сенсорный многопозиционный ввод

50

Многопозиционный ввод — способ взаимодействия с приложением с помощью касания экрана. Он отличается от более традиционного перьевого ввода тем, что позволяет пользователю работать сразу несколькими пальцами. В наиболее сложном варианте многопозиционный ввод распознает жесты — особые способы движения нескольких пальцев пользователя как команды для выполнения одной из распространенных операций. Например, если прикоснуться к сенсорному экрану двумя пальцами, а затем свести их, то обычно это означает "уменьшить масштаб", а движение одного пальца вокруг другого означает "повернуть".

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

Список стандартных многопозиционных жестов, которые распознает Windows 7, приведен по адресу tinyurl.com/yawwhw2.

Многие разработчики считают, что многопозиционный ввод постепенно станет обычным делом при взаимодействии с приложениями, по крайней мере, на таких устройствах, как настольные компьютеры и ноутбуки. Но пока поддержка подобного ввода ограничена небольшим набором сенсорных планшетов, настольных моноблоков и ЖК-мониторов. На момент написания этих строк список оборудования, поддерживающего многопозиционный ввод, находится по адресу tinyurl.com/y8pnsbu.

Это осложняет жизнь разработчикам, которые хотят опробовать работу с многопозиционными приложениями. Пока лучшее, что можно посоветовать — купить дешевый ноутбук с многопозиционным сенсорным экраном. Однако, если приложить немного усилий, можно использовать эмулятор для моделирования многопозиционного ввода. Для этого необходимо подключить к компьютеру больше одной мыши и установить драйверы из проекта Multi-Touch Vista (с открытым исходным кодом, работает и с Windows 7). Для начала рекомендую вам зайти на сайт http://multitouchvista.codeplex.com. Однако предупреждаю: возможно, вам придется пройти проверку с помощью обучающих видеофильмов, чтобы удостовериться, что (довольно запутанная) процедура установки выполнена правильно.

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

Уровни поддержки многопозиционного ввода

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

Простое касание

Это самый нижний уровень поддержки; он дает доступ ко всем касаниям пользователя. Его недостаток в том, что ваше приложение должно само сопоставлять отдельные сообщения касаний и интерпретировать их. Обработка простых касаний имеет смысл, если вы собираетесь не распознавать стандартные жесты, а реагировать на одновременные касания особым образом. Примером может служить программа рисования наподобие Windows 7 Paint, которая позволяет пользователям "рисовать" на сенсорном экране сразу несколькими пальцами.

Манипуляции

Этот уровень абстракции удобен для интерпретации простых касаний в виде осмысленных жестов — примерно так же, как элементы WPF интерпретируют последовательность событий MouseDown и MouseUp как событие более высокого уровня MouseDoubleClick. Элементы WPF обычно распознают прокрутку, изменение масштаба, вращение и постукивание.

Поддержка, встроенная в элементы

Некоторые элементы уже реагируют на многопозиционные события, без необходимости написание специального кода. Например, элементы управления с прокруткой — такие как ListBox, ListView, DataGrid, TextBox и ScrollViewer — поддерживают пальцевую прокрутку.

Простые касания

Как и в случае базовых событий мыши и клавиатуры, события касания встроены в низкоуровневые классы UIElement и ContentElement. Все они перечислены ниже.

События сенсорного ввода
Имя Тип маршрутизации Описание
PreviewTouchDown Туннелирование Возникает, когда пользователь касается данного элемента
TouchDown Пузырьковое распространение То же
PreviewTouchMove Туннелирование Возникает, когда пользователь коснулся данного элемента и передвигает палец
TouchMove Пузырьковое распространение То же
PreviewTouchUp Туннелирование Возникает, когда пользователь поднимает палец, прекращая касание
TouchUp Пузырьковое распространение То же
TouchEnter Нет Возникает, когда точка контакта входит извне в пределы данного элемента
TouchLeave Нет Возникает, когда точка контакта выходит за пределы данного элемента

Все эти события предоставляют объект TouchEventArgs, который содержит два важных члена. Во-первых, метод GetTouchPoint() дает экранные координаты точки, где возникло событие касания (и еще другие, не так часто применяемые, данные — такие как размер точки контакта). Во-вторых, свойство TouchDevice возвращает объект TouchDevice. Фокус в том, что каждая точка контакта считается отдельным устройством. Поэтому если пользователь касается экрана двумя пальцами в разных местах (одновременно или один за другим), WPF считает, что это два устройства касания, и назначает каждому из них уникальный идентификатор. При перемещении пальцев и возникновения события касания код может различить эти точки касания с помощью свойства TouchDevice.Id.

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

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

<Canvas Name="cnv" TouchDown="cnv_TouchDown" TouchUp="cnv_TouchUp" 
        TouchMove="cnv_TouchMove" Background="LightBlue"></Canvas>
public partial class MainWindow : Window
{
        public MainWindow()
        {
            InitializeComponent();
        }

        // Задаем через словарь коллекцию элипсов
        private Dictionary<int, UIElement> myEllipses =
            new Dictionary<int, UIElement>();

        // Обрабатываем касание пальцев
        private void cnv_TouchDown(object sender, TouchEventArgs e)
        {
            // Создаем новый овал 
            Ellipse ellipse = new Ellipse();
            ellipse.Width = 30;
            ellipse.Height = 30;
            ellipse.Stroke = Brushes.White;
            ellipse.Fill = Brushes.Gold;

            // Помещаем овал в точку касания
            TouchPoint tp = e.GetTouchPoint(cnv);
            Canvas.SetTop(ellipse, tp.Bounds.Top);
            Canvas.SetLeft(ellipse, tp.Bounds.Left);

            // Сохраняем овал в словаре
            myEllipses[e.TouchDevice.Id] = ellipse;

            // Отражаем овал в канве
            cnv.Children.Add(ellipse);
        }

        // Обрабатываем событие движения пальцев
        private void cnv_TouchMove(object sender, TouchEventArgs e)
        {
            UIElement el = myEllipses[e.TouchDevice.Id];

            // Перемещаем овал в новую точку
            TouchPoint tp = e.GetTouchPoint(cnv);
            Canvas.SetTop(el, tp.Bounds.Top);
            Canvas.SetLeft(el, tp.Bounds.Left);
        }

        // Удаляем овал с холста при отпускании пальцев
        // с сенсорного экрана
        private void cnv_TouchUp(object sender, TouchEventArgs e)
        {
            UIElement el = myEllipses[e.TouchDevice.Id];
            cnv.Children.Remove(el);
            myEllipses.Remove(e.TouchDevice.Id);
        }        
}
Сенсорный ввод WPF

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

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

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

Пройди тесты
Лучший чат для C# программистов