WPF StackPanel带有单击和双击功能

9

我需要处理WPF StackPanel上的双击事件单击事件。但是StackPanel没有DoubleClick事件。

我想在这两个事件处理程序中执行2个不同的操作。

有什么好的方法吗?

谢谢


9
我仍然无法理解微软的人员如何会忽略这样一个基本功能。 - Adolfo Perez
7个回答

31
 <StackPanel MouseDown="StackPanel_MouseDown">
   <!--stackpanel content-->
    <TextBlock>Hello</TextBlock>
</StackPanel>

然后在事件处理程序中:

 private void StackPanel_MouseDown(object sender, MouseButtonEventArgs e)
    {
        if (e.ClickCount >= 2)
        { 
            string hello; //only hit here on double click  
        }
    }

应该能够工作。请注意,在 StackPanel 中单击会触发事件(但不符合 if 检查)。


不如处理双击事件那么高效,但在没有双击事件时足够快。+1 - Randolpho
是的,我已经尝试过了。但我需要处理单击和双击两种情况。clickcount并不能真正帮助我,因为似乎双击也等同于单击。 - theSpyCry
1
@PaN1C_Showt1Me 你觉得这样怎么样:'if (e.ClickCount >= 2) { HandleDoubelClick(); } else if (e.ClickCount >= 1) {HandleSingleClick();}' - joce
这与Troll带来的是一样的...问题是,点击计数从未>= 2。 - theSpyCry

16

...年后。@MoominTroll的解决方案完全可行。另一个选项是将堆栈面板包装在支持双击事件的内容控件中。

<ContentControl MouseDoubleClick="DoubleClickHandler" >
    <StackPanel>

    </StackPanel>
</ContentControl>

最好的。对我来说,优雅地解决了。 - Vico

4

另一种选项是将MouseBinding添加到StackElement的InputBindings中,然后再添加一个由MouseBinding激活的CommandBinding。总的来说,这比基于事件的机制更好,因为它避免了强引用导致的内存泄漏问题。它还提供了命令逻辑与表示之间的分离。

尽管如此,与事件相关联并不像直接那么简单,但可以作为快捷方式。

不言而喻,将StackPanel背景至少设为透明色,否则当您单击“背景”时,它将无法被鼠标单击命中检测捕捉到。空背景将被命中检测跳过。


2

最好的方法是编写自己的鼠标按钮处理程序,并设置超时时间 - 如果事件在超时期内再次触发,则触发您的双击消息,否则调用单击处理程序。这里有一些示例代码 (编辑: 最初在这里找到):

/// <summary>
/// For double clicks
/// </summary>
public class MouseClickManager {
    private event MouseButtonEventHandler _click;
    private event MouseButtonEventHandler _doubleClick;

    public event MouseButtonEventHandler Click {
        add { _click += value; }
        remove { _click -= value; }
    }

    public event MouseButtonEventHandler DoubleClick {
        add { _doubleClick += value; }
        remove { _doubleClick -= value; }
    }

    /// <summary>
    /// Gets or sets a value indicating whether this <see cref="MouseClickManager"/> is clicked.
    /// </summary>
    /// <value><c>true</c> if clicked; otherwise, <c>false</c>.</value>
    private bool Clicked { get; set; }

    /// <summary>
    /// Gets or sets the timeout.
    /// </summary>
    /// <value>The timeout.</value>
    public int DoubleClickTimeout { get; set; }

    /// <summary>
    /// Initializes a new instance of the <see cref="MouseClickManager"/> class.
    /// </summary>
    /// <param name="control">The control.</param>
    public MouseClickManager(int doubleClickTimeout) {
        this.Clicked = false;
        this.DoubleClickTimeout = doubleClickTimeout;
    }

    /// <summary>
    /// Handles the click.
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
    public void HandleClick(object sender, MouseButtonEventArgs e) {
        lock (this) {
            if (this.Clicked) {
                this.Clicked = false;
                OnDoubleClick(sender, e);
            }
            else {
                this.Clicked = true;
                ParameterizedThreadStart threadStart = new ParameterizedThreadStart(ResetThread);
                Thread thread = new Thread(threadStart);
                thread.Start(e);
            }
        }
    }

    /// <summary>
    /// Resets the thread.
    /// </summary>
    /// <param name="state">The state.</param>
    private void ResetThread(object state) {
        Thread.Sleep(this.DoubleClickTimeout);

        lock (this) {
            if (this.Clicked) {
                this.Clicked = false;
                OnClick(this, (MouseButtonEventArgs)state);
            }
        }
    }

    /// <summary>
    /// Called when [click].
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
    private void OnClick(object sender, MouseButtonEventArgs e) {
        if (_click != null) {
            if (sender is Control) {
                (sender as Control).Dispatcher.BeginInvoke(_click, sender, e);
            }
        }
    }

    /// <summary>
    /// Called when [double click].
    /// </summary>
    /// <param name="sender">The sender.</param>
    /// <param name="e">The <see cref="System.Windows.Input.MouseButtonEventArgs"/> instance containing the event data.</param>
    private void OnDoubleClick(object sender, MouseButtonEventArgs e) {
        if (_doubleClick != null) {
            _doubleClick(sender, e);
        }
    }
}

然后,在您希望接收事件的控件中:

MouseClickManager fMouseManager = new MouseClickManager(200);
fMouseManager.Click += new MouseButtonEventHandler(YourControl_Click); 
fMouseManager.DoubleClick += new MouseButtonEventHandler(YourControl_DoubleClick);

@MoominTroll 哈哈,恰好我刚在我们的应用程序中实现了这个:D - Mark Pim
5
我一定读错了...每次单击鼠标时都会开始一个新的线程,而且这不是双击?哇...那真是...呃...特别。请考虑使用“e.ClickCount >= 2”的以下解决方案。 (使用“e.ClickCount >= 2”作为解决方法) - joce
1
我不知道如何将这些事件分配给控件。 - Amged
1
通常情况下,避免锁定公共类型或超出您代码控制范围的实例。如果该实例可以公开访问,则“lock(this)”是一个问题。 - gliderkite

0

我曾经遇到类似的问题,需要响应单击事件并在双击事件的情况下执行附加操作。我通过以下方法解决了这个问题:

1)定义并声明一些整数来保存上次点击的时间戳

int lastClickTimestamp;

2) 在 Window_Loaded 方法中,使用比200大的某个数字初始化先前声明的变量

lastClickTimestamp = 1000;

3) 将鼠标按钮处理程序添加到您的堆栈面板

stackPanel.MouseLeftButtonUp += new MouseButtonEventHandler(stackPanel_MouseLeftButtonUp);

4) 添加以下方法

void stackPanel_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        if (e.Timestamp - lastClickTimeStamp < 200)
        {
            //double click
        }
        lastClickTimeStamp = e.Timestamp;

        //single click
    }

如果你需要分别检测单击和双击事件,那么这段代码就没用了。这种情况会增加一些复杂性,但肯定可以解决。


0

使用 WPF DispatcherTimer 完成答案。我们按需创建计时器,并在完成后断开连接,以防止资源堵塞。

C# 代码:

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;

public partial class MainWindow : Window
{
    DispatcherTimer dt;

    bool clear_timer()
    {
        if (dt == null)
            return false;
        dt.Tick -= _single_click;
        dt = null;
        return true;
    }

    private void _click(Object sender, MouseButtonEventArgs e)
    {
        if (clear_timer())
            Debug.Print("double click");
        else
            dt = new DispatcherTimer(
                        TimeSpan.FromMilliseconds(GetDoubleClickTime()),
                        DispatcherPriority.Normal,
                        _single_click,
                        Dispatcher);
    }

    void _single_click(Object sender, EventArgs e)
    {
        clear_timer();
        Debug.Print("single click");
    }

    public MainWindow() { InitializeComponent(); }

    [DllImport("user32.dll")]
    static extern uint GetDoubleClickTime();
 };

XAML:

<Window x:Class="MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel Orientation="Horizontal"
                Background="AliceBlue"
                Width="100"
                Height="100"
                MouseLeftButtonDown="_click" />
</Window>

0

有一个更简单的解决方案来实现这个。

在 StackPanel 的事件 PreviewMouseLeftDown(例如),你可以检查 MouseButtonEventArgs.ClickCount 属性是否为 2。 1 = 单击 2 = 双击


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接