diff --git a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems
index c0b60db6..f9bdcc45 100644
--- a/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems
+++ b/src/Shared/HandyControl_Shared/HandyControl_Shared.projitems
@@ -342,6 +342,7 @@
+
diff --git a/src/Shared/HandyControl_Shared/Tools/Helper/Input/InputClickHelper.cs b/src/Shared/HandyControl_Shared/Tools/Helper/Input/InputClickHelper.cs
new file mode 100644
index 00000000..7119cd3f
--- /dev/null
+++ b/src/Shared/HandyControl_Shared/Tools/Helper/Input/InputClickHelper.cs
@@ -0,0 +1,163 @@
+using System;
+using System.Windows;
+using System.Windows.Input;
+
+namespace HandyControl.Tools.Input
+{
+ ///
+ /// 输入层点击帮助类
+ ///
+ public static class InputClickHelper
+ {
+ ///
+ /// 将 MouseDown MouseMove MouseUp 封装为点击事件
+ ///
+ /// 要被附加的元素
+ /// 点击的事件
+ /// 因为拖动而结束点击时触发
+ public static void AttachMouseDownMoveUpToClick(UIElement element, EventHandler clickEventHandler,
+ EventHandler dragStarted = null)
+ {
+ var inputInfo = GetOrCreateInputInfo(element);
+ inputInfo.ClickEventHandler += clickEventHandler;
+
+ inputInfo.DragStarted += dragStarted;
+
+ element.MouseDown -= Element_MouseDown;
+ element.MouseDown += Element_MouseDown;
+ element.MouseMove -= Element_MouseMove;
+ element.MouseMove += Element_MouseMove;
+ element.MouseUp -= Element_MouseUp;
+ element.MouseUp += Element_MouseUp;
+ element.LostMouseCapture -= Element_LostMouseCapture;
+ element.LostMouseCapture += Element_LostMouseCapture;
+ }
+
+ ///
+ /// 去掉对 的点击时间的监听
+ ///
+ ///
+ /// 点击的事件
+ /// 因为拖动而结束点击时触发的事件
+ public static void DetachMouseDownMoveUpToClick(UIElement element, EventHandler clickEventHandler,
+ EventHandler dragStarted = null)
+ {
+ var inputInfo = GetInputInfo(element);
+ if (inputInfo == null)
+ {
+ return;
+ }
+
+ inputInfo.ClickEventHandler -= clickEventHandler;
+ inputInfo.DragStarted -= dragStarted;
+
+ if (inputInfo.IsEmpty())
+ {
+ element.ClearValue(InputInfoProperty);
+ element.MouseDown -= Element_MouseDown;
+ element.MouseMove -= Element_MouseMove;
+ element.MouseUp -= Element_MouseUp;
+ element.LostMouseCapture -= Element_LostMouseCapture;
+ }
+ }
+
+ private static void Element_LostMouseCapture(object sender, MouseEventArgs e)
+ {
+ var element = (UIElement) sender;
+ GetInputInfo(element)?.LostCapture();
+ }
+
+ private static void Element_MouseUp(object sender, MouseButtonEventArgs e)
+ {
+ var element = (UIElement) sender;
+
+ GetInputInfo(element)?.Up(e.GetPosition(element));
+ }
+
+ private static void Element_MouseMove(object sender, MouseEventArgs e)
+ {
+ var element = (UIElement) sender;
+
+ GetInputInfo(element)?.Move(e.GetPosition(element));
+ }
+
+ private static void Element_MouseDown(object sender, MouseButtonEventArgs e)
+ {
+ var element = (UIElement) sender;
+
+ GetInputInfo(element)?.Down(e.GetPosition(element));
+ }
+
+ private static readonly DependencyProperty InputInfoProperty = DependencyProperty.RegisterAttached(
+ "InputInfo", typeof(InputInfo), typeof(InputClickHelper), new PropertyMetadata(default(InputInfo)));
+
+ private static InputInfo GetOrCreateInputInfo(UIElement element)
+ {
+ var inputInfo = GetInputInfo(element);
+ if (inputInfo == null)
+ {
+ inputInfo = new InputInfo();
+ SetInputInfo(element, inputInfo);
+ }
+
+ return inputInfo;
+ }
+
+ private static void SetInputInfo(DependencyObject element, InputInfo value)
+ {
+ element.SetValue(InputInfoProperty, value);
+ }
+
+ private static InputInfo GetInputInfo(DependencyObject element)
+ {
+ return (InputInfo) element.GetValue(InputInfoProperty);
+ }
+
+ private class InputInfo
+ {
+ public void Down(Point position)
+ {
+ _downedPosition = position;
+ _isClick = true;
+ }
+
+ public void Move(Point position)
+ {
+ if (!_isClick) return;
+
+ if ((position - _downedPosition).LengthSquared > ToleranceSquared)
+ {
+ _isClick = false;
+ DragStarted?.Invoke(null, EventArgs.Empty);
+ }
+ }
+
+ public void Up(Point position)
+ {
+ _isClick = _isClick
+ && (position - _downedPosition).LengthSquared <= ToleranceSquared;
+
+ if (!_isClick) return;
+
+ ClickEventHandler?.Invoke(null, EventArgs.Empty);
+
+ _isClick = false;
+ }
+
+ public void LostCapture()
+ {
+ _isClick = false;
+ }
+
+ public double ToleranceSquared { set; get; } = 0.01;
+
+ public event EventHandler ClickEventHandler;
+ public event EventHandler DragStarted;
+
+ public bool IsEmpty() => ClickEventHandler is null && DragStarted is null;
+
+ private Point _downedPosition;
+ private bool _isClick;
+ }
+ }
+}
\ No newline at end of file