实现了一个支持长短按得按钮组件,单击可以触发Click事件,长按可以触发LongPressed事件,长按松开时触发LongClick事件。源码请自取:Github

长按阈值属性的建立

为了方便在xaml中使用,我们先配置一个DependencyProperty叫做LongPressTime来作为界定长按的阈值

1
2
3
4
5
6
7
8
public class LongPressButtonEx : Button { public static readonly DependencyProperty LongPressTimeProperty = DependencyProperty.Register("LongPressTime", typeof(int), typeof(LongPressButtonEx), new PropertyMetadata(500)); public int LongPressTime { set => SetValue(LongPressTimeProperty, value); get => (int)GetValue(LongPressTimeProperty); } }

定义完成后可以在Xaml设计器中使用LongPressTime这个拓展属性

1
2
3
4
5
6
7
8
9
<Window x:Class="LongPressButton.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:LongPressButton" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid> <local:LongPressButtonEx Width="96" Height="48" LongPressTime="200"> Button </local:LongPressButtonEx> </Grid> </Window>

长按的定时器判定方法

C#中的4种定时器,在WPF中需要使用Dispater Timer

定义一个DispatcherTimer来监控是否按下达到了长按

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private DispatcherTimer _pressDispatcherTimer; private void OnDispatcherTimeOut(object sender, EventArgs e) { _pressDispatcherTimer?.Stop(); Debug.WriteLine($"Timeout {LongPressTime}"); } protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { base.OnMouseLeftButtonDown(e); Debug.WriteLine("Button: Mouse down."); if (_pressDispatcherTimer == null) { _pressDispatcherTimer = new DispatcherTimer(); _pressDispatcherTimer.Tick += OnDispatcherTimeOut; _pressDispatcherTimer.Interval = new TimeSpan(0, 0, 0, 0, LongPressTime); _pressDispatcherTimer.Start(); Debug.WriteLine("Button: Timer started"); } } protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) { base.OnMouseLeftButtonUp(e); Debug.WriteLine("Button: Mouse up."); _pressDispatcherTimer?.Stop(); _pressDispatcherTimer = null; }

现在分别点击和长按按钮可以看到调试输出

1
2
3
4
5
6
7
... # 点击 Button: Mouse down. Button: Timer started Button: Mouse up. # 长按 Button: Mouse down. Button: Timer started Timeout 200 Button: Mouse up.

实现长按事件的定义

现在作为一个自定义控件,我们需要在长按后发出一个RoutedEvent,并修改部分之前的代码抛出事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/// <summary> /// LongPress Routed Event /// </summary> public static readonly RoutedEvent LongPressEvent = EventManager.RegisterRoutedEvent("LongPress", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(LongPressButtonEx)); public event RoutedEventHandler LongPress { add => AddHandler(LongPressEvent, value); remove => RemoveHandler(LongPressEvent, value); } private void OnDispatcherTimeOut(object sender, EventArgs e) { _pressDispatcherTimer?.Stop(); Debug.WriteLine($"Timeout {LongPressTime}"); RaiseEvent(new RoutedEventArgs(LongPressEvent)); // raise the long press event }

回到窗体的代码中,添加事件的响应

1
2
3
4
<local:LongPressButtonEx Height="48" Width="256" LongPressTime="200" LongPress="LongPressButtonEx_LongPress" Click="LongPressButtonEx_Click"> Click or Long Press Me! </local:LongPressButtonEx>

C#代码如下,长按按钮会显示Long Pressed,单击会是Click

1
2
3
4
5
6
7
8
9
10
private void LongPressButtonEx_LongPress(object sender, RoutedEventArgs e) { if (sender is LongPressButtonEx btn) { btn.Content = "Long Pressed"; } } private void LongPressButtonEx_Click(object sender, RoutedEventArgs e) { if (sender is LongPressButtonEx btn) { btn.Content = "Clicked"; } }

ASURh3xnxQGSsQEpmKNpb__fNsWPuMWHRS0STa7OriA.gif

发现ClickLongPress都可以响应,但是当松开按钮时又变成了Click,原因是鼠标松开时响应了默认的Click事件

现在对按钮控件默认的OnClick函数稍作修改,可以让Click也不出问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/// <summary> /// DependencyProperty for IsLongPress /// </summary> public static readonly DependencyProperty IsLongPressProperty = DependencyProperty.Register("IsLongPress", typeof(bool), typeof(LongPressButtonEx), new PropertyMetadata(false)); public bool IsLongPress { set => SetValue(IsLongPressProperty, value); get => (bool)GetValue(IsLongPressProperty); } private void OnDispatcherTimeOut(object sender, EventArgs e) { IsLongPress = true; _pressDispatcherTimer?.Stop(); Debug.WriteLine($"Timeout {LongPressTime}"); RaiseEvent(new RoutedEventArgs(LongPressEvent)); // raise the long press event } protected override void OnClick() { if (!IsLongPress) { base.OnClick(); } else { RaiseEvent(new RoutedEventArgs(LongPressReleaseEvent)); // raise the long press event IsLongPress = false; } }

之后再进行点击操作,我们就可以看到符合预期的结果

ha7_24GltxuES2RpK63icyN03HgVglo5i4vjmyhUtLE.gif

长按+Style按钮的展示效果
外观Style自定义见这篇文章:WPF自定义按钮外形
3rYXD4vVuKScH5rArLv9ZE0LSLR8n0XsHtDmHCdL27c.gif

参考链接

UIElement.MouseLeftButtonDown Event
用户控件自定义 DependencyProperty 属性使用教程
WPF 中 DispatcherTimer 计时器
如何:创建自定义路由事件
WPF 自定义带自定义参数路由事件
Use WPF Style in another assemble