Simple WPF: WPF 实现按钮的长按,短按功能

最新内容优先发布于个人博客:小虎技术分享站,随后逐步搬运到博客园。

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

长按阈值属性的建立

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

C# 复制代码
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这个拓展属性

xml 复制代码
<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来监控是否按下达到了长按

C# 复制代码
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;
}

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

Plain 复制代码
...
# 点击
Button: Mouse down.
Button: Timer started
Button: Mouse up.
# 长按
Button: Mouse down.
Button: Timer started
Timeout 200
Button: Mouse up.

实现长按事件的定义

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

C# 复制代码
/// <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
}

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

C# 复制代码
<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

C# 复制代码
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";
    }
}

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

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

C# 复制代码
/// <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;
    }
}

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

长按+Style按钮的展示效果

外观Style自定义见这篇文章:WPF自定义按钮外形

参考链接

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

相关推荐
未来之窗软件服务8 小时前
幽冥大陆(二)RDIFSDK 接口文档:布草洗涤厂高效运营的技术桥梁C#—东方仙盟
开发语言·c#·rdif·仙盟创梦ide·东方仙盟
1uther9 小时前
Unity核心概念⑨:Screen
开发语言·游戏·unity·c#·游戏引擎
阿幸软件杂货间10 小时前
Office转PDF转换器v1.0.py
开发语言·pdf·c#
sali-tec10 小时前
C# 基于halcon的视觉工作流-章34-环状测量
开发语言·图像处理·算法·计算机视觉·c#
玉面小君10 小时前
从 WPF 到 Avalonia 的迁移系列实战篇6:Trigger、MultiTrigger、DataTrigger 的迁移
wpf·avalonia
Tiger_shl11 小时前
【层面一】C#语言基础和核心语法-02(反射/委托/事件)
开发语言·c#
mudtools15 小时前
.NET驾驭Word之力:COM组件二次开发全攻略之连接Word与创建你的第一个自动化文档
后端·c#
王维志16 小时前
LiteDB详解
数据库·后端·mongodb·sqlite·c#·json·database
程序猿多布17 小时前
XLua教程之热补丁技术
unity·c#·lua·xlua
咕白m62518 小时前
C# Excel 读取入门教程:免费实现方法
c#·.net