そんなに難しいことでもなく、Touch系のイベントで来るTouchEventArgsのTouchDeviceがイベントを起こした個々のデバイス(指)を示すので、このIdを記録して、これが一連のイベントが終わるまでに一つしか来ていなければシングルタッチ、複数来ていればマルチタッチと判別できます。
以下のButtonの例では、PreviewTouchDownイベントごとにTouchDeviceのIdをHashSetに記録していき、Clickイベントのときに複数来ているか判別した後で、HashSetを初期化しています。なお、タッチ操作の終わりに常にClickイベントが来るわけではないので、PreviewTouchUpイベントを引っ掛けた上で、これはClickイベントより先に来るので、1秒の猶予をおいてから初期化するようにしています。
より実用的に、ClickイベントからBehaviorのCallMethodActionを実行するようにしている場合、そのIsEnabled依存関係プロパティとbindingを張ってもいいですが、そのために妙にコードが増えるのもうまくないので、CallMethodActionの分も含めてBehaviorにまとめたのが以下。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Reflection; | |
using System.Threading.Tasks; | |
using System.Windows; | |
using System.Windows.Controls.Primitives; | |
using System.Windows.Input; | |
using Microsoft.Xaml.Behaviors; | |
public class MultiTouchBehavior : Behavior<ButtonBase> | |
{ | |
public object TargetObject | |
{ | |
get { return (object)GetValue(TargetObjectProperty); } | |
set { SetValue(TargetObjectProperty, value); } | |
} | |
public static readonly DependencyProperty TargetObjectProperty = | |
DependencyProperty.Register( | |
"TargetObject", | |
typeof(object), | |
typeof(MultiTouchBehavior), | |
new PropertyMetadata( | |
null, | |
(d, e) => ((MultiTouchBehavior)d).SetMethods())); | |
public string SingleTouchClickMethodName { get; set; } | |
public string MultiTouchClickMethodName { get; set; } | |
private MethodInfo _singleTouchClickMethod; | |
private MethodInfo _multiTouchClickMethod; | |
private void SetMethods() | |
{ | |
if (TargetObject is null) | |
return; | |
var targetType = TargetObject.GetType(); | |
if (!string.IsNullOrEmpty(SingleTouchClickMethodName)) | |
_singleTouchClickMethod = targetType.GetMethod(SingleTouchClickMethodName, Type.EmptyTypes); | |
if (!string.IsNullOrEmpty(MultiTouchClickMethodName)) | |
_multiTouchClickMethod = targetType.GetMethod(MultiTouchClickMethodName, Type.EmptyTypes); | |
} | |
protected override void OnAttached() | |
{ | |
base.OnAttached(); | |
this.AssociatedObject.PreviewTouchDown += OnPreviewTouchDown; | |
this.AssociatedObject.PreviewTouchUp += OnPreviewTouchUp; | |
this.AssociatedObject.Click += OnClick; | |
} | |
protected override void OnDetaching() | |
{ | |
base.OnDetaching(); | |
this.AssociatedObject.PreviewTouchDown -= OnPreviewTouchDown; | |
this.AssociatedObject.PreviewTouchUp -= OnPreviewTouchUp; | |
this.AssociatedObject.Click -= OnClick; | |
} | |
private readonly HashSet<int> _touchDeviceIds = new HashSet<int>(); | |
private void OnPreviewTouchDown(object sender, TouchEventArgs e) | |
{ | |
_touchDeviceIds.Add(e.TouchDevice.Id); | |
} | |
private void OnPreviewTouchUp(object sender, TouchEventArgs e) | |
{ | |
Task.Run(async () => | |
{ | |
await Task.Delay(TimeSpan.FromSeconds(1)); | |
_touchDeviceIds.Clear(); | |
}); | |
} | |
private void OnClick(object sender, RoutedEventArgs e) | |
{ | |
var isMultiTouch = (_touchDeviceIds.Count > 1); | |
_touchDeviceIds.Clear(); | |
if (!isMultiTouch) | |
{ | |
_singleTouchClickMethod?.Invoke(TargetObject, null); | |
} | |
else | |
{ | |
_multiTouchClickMethod?.Invoke(TargetObject, null); | |
} | |
} | |
} |