Simple WPF: WPF 實現按鈕的長按,短按功能

mrchip發表於2024-07-08

最新內容優先釋出於個人部落格:小虎技術分享站,隨後逐步搬運到部落格園。

實現了一個支援長短按得按鈕元件,單擊可以觸發Click事件,長按可以觸發LongPressed事件,長按鬆開時觸發LongClick事件。原始碼請自取:Github

長按閾值屬性的建立

為了方便在xaml中使用,我們先配置一個DependencyProperty叫做LongPressTime來作為界定長按的閾值

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這個擴充屬性

<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來監控是否按下達到了長按

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;
}

現在分別點選和長按按鈕可以看到除錯輸出

...
# 點選
Button: Mouse down.
Button: Timer started
Button: Mouse up.
# 長按
Button: Mouse down.
Button: Timer started
Timeout 200
Button: Mouse up.

實現長按事件的定義

現在作為一個自定義控制元件,我們需要在長按後發出一個RoutedEvent,並修改部分之前的程式碼丟擲事件

/// <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
}

回到窗體的程式碼中,新增事件的響應

<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

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";
    }
}

image

發現ClickLongPress都可以響應,但是當鬆開按鈕時又變成了Click,原因是滑鼠鬆開時響應了預設的Click事件

現在對按鈕控制元件預設的OnClick函式稍作修改,可以讓Click也不出問題

/// <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;
    }
}

之後再進行點選操作,我們就可以看到符合預期的結果

image

長按+Style按鈕的展示效果
外觀Style自定義見這篇文章:WPF自定義按鈕外形
image

參考連結

UIElement.MouseLeftButtonDown Event
使用者控制元件自定義 DependencyProperty 屬性使用教程
WPF 中 DispatcherTimer 計時器
如何:建立自定義路由事件
WPF 自定義帶自定義引數路由事件
Use WPF Style in another assemble

相關文章