сенсорный ввод с использованием WPF/C#: как определить порог, чтобы отличить клики от панорамирования

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

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

Есть ли способ определить порог движения, который Windows не будет интерпретировать как жест панорамирования, а вместо этого распознает его как щелчок?


person Erik    schedule 28.04.2016    source источник
comment
Я понятия не имею, используется ли он на сенсорных экранах, но с операциями перетаскивания мышью существует значение SystemParameters.MinimumHorizontalDragDistance, используемое для определения минимальной суммы, которую вы должны перетащить, прежде чем запускать операцию перетаскивания. Возможно, есть что-то подобное для сенсорных интерфейсов?   -  person Rachel    schedule 28.04.2016


Ответы (1)


Я придумал эту реализацию - 32 пикселя используются для определения порога.

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using Willbe.Extensions;

namespace Kiolyn.Wpf
{
  public static class TouchAssist
  {
    public static bool GetTouchAsClick(DependencyObject obj)
      => (bool)obj.GetValue(TouchAsClickProperty);

    public static void SetTouchAsClick(DependencyObject obj, bool value)
      => obj.SetValue(TouchAsClickProperty, value);

    // Using a DependencyProperty as the backing store for SelectOnMouseOver.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty TouchAsClickProperty =
        DependencyProperty.RegisterAttached(
          "TouchAsClick",
          typeof(bool),
          typeof(TouchAssist),
          new UIPropertyMetadata(false, OnTouchAsClickChanged));

    // Using a DependencyProperty as the backing store for SelectOnMouseOver.  This enables animation, styling, binding, etc...
    private static readonly DependencyProperty LastExecuteTimeProperty =
        DependencyProperty.RegisterAttached(
          "LastExecuteTime",
          typeof(DateTime),
          typeof(TouchAssist),
          new UIPropertyMetadata(null));

    private static readonly DependencyProperty TouchDownPositionProperty =
        DependencyProperty.RegisterAttached(
          "TouchDownPosition",
          typeof(Point),
          typeof(TouchAssist),
          new UIPropertyMetadata(null));

    static void OnTouchAsClickChanged(DependencyObject d, DependencyPropertyChangedEventArgs ev)
    {
      if (d is Button button)
      {
        button.IsManipulationEnabled = true;

        button.TouchDown += (s, e) =>
        {
          button.RaiseEvent(new MouseButtonEventArgs(Mouse.PrimaryDevice, e.Timestamp, MouseButton.Left)
          {
            RoutedEvent = Mouse.PreviewMouseDownEvent
          });
          button.ClearValue(LastExecuteTimeProperty);
          button.SetValue(TouchDownPositionProperty, e.GetTouchPoint(Application.Current.MainWindow).Position);
        };
        button.TouchUp += (s, e) =>
        {
          var command = button.Command;
          var parameter = button.CommandParameter;
          if (command != null && command.CanExecute(parameter) && button.GetValue(TouchDownPositionProperty) is Point touchDownPosition)
          {
            var touchUpPosition = e.GetTouchPoint(Application.Current.MainWindow).Position;
            var dx = Math.Abs(touchUpPosition.X - touchDownPosition.X);
            var dy = Math.Abs(touchUpPosition.Y - touchDownPosition.Y);
            if (dx < 32 && dy < 32)
            {
              // Should be treated as click
              // Mark the last execute time
              button.SetValue(LastExecuteTimeProperty, DateTime.Now);
              command.Execute(parameter);
            }
          }
        };
        button.PreviewMouseLeftButtonUp += (s, e) =>
        {
          if (button.GetValue(LastExecuteTimeProperty) is DateTime lastExecuteTime && DateTime.Now.Subtract(lastExecuteTime).Milliseconds < 300)
          {
            e.Handled = true;
            button.ReleaseMouseCapture();
          }
          button.ClearValue(LastExecuteTimeProperty);
        };
      }
    }
  }
}

Использование:

<Button wpf:TouchAssist.TouchAsClick="True"/>
person Chinh Nguyen    schedule 09.11.2018