双击发生时防止单击 WPF 控件

Prevent single click on WPF control when double click occured

我有 Image 控件,并处理了 PreviewMouseLeftButtonDown 事件。 其逻辑是在 单击 时更改图像内容,在 双击 时激活其他视觉样式。

我知道 ClickCount 属性 就像一些答案所说的 (e.g. this) 并且成功区分 single/double 次点击,但问题是单击 always,无论下一刻双击跟随还是不跟随(这很公平,无论如何)。因此,双击处理了两个动作——单击和下一刻双击。

问题:是否有任何方法可以防止单击 before 发生在双击之后,除了 处理这种情况某种计时器魔法?

编辑:
我发现 comment 的问题很好,这与 windows 资源管理器类比 - 单击选定文件后它如何等待,并开始重命名只是确保在第一次单击后没有其他单击发生。
延迟将 肯定 存在的目的是为了解决这个问题,但这是否意味着 windows explorer 使用精确的计时器,或者它可能有一些其他选项(一些 属性或可以等待的事件)以在发生双击时保持单击?

我猜你需要使用计时器。要获得仍然发生双击的有效最长时间,您可以使用以下函数(已测试;输出为 500 毫秒):

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

(来源:how to get the double-click-time in WPF

通常当您有多个值想要绑定到一个 WPF 控件时,您可以使用 ItemsSource 之类的东西并将其绑定到视图模型中的列表。但我想这对图像控制不起作用。因此,您应该使用计时器并将上述函数的值用于您的计时器。

最后没有收到与计时器相关的解决方案的建议(我也没有找到任何建议),所以这里是一个简单的例子,如何在双击发生时防止单击。

Xaml:

<Window x:Class="Whosebug.DoubleClickExample.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="100" Width="150"
    MouseDown="RootElement_OnMouseDown">
</Window>

代码隐藏:

namespace Whosebug.DoubleClickExample
{
    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Threading.Tasks;
    using System.Windows;
    using System.Windows.Input;

    public partial class MainWindow : Window
    {
        [DllImport("user32.dll")]
        public static extern uint GetDoubleClickTime();

        public MainWindow()
        {
            this.InitializeComponent();
        }

        private Guid lastGuid = Guid.Empty;

        private void RootElement_OnMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount == 1)
            {
                // Create new unique id and save it into field.
                var guid = Guid.NewGuid();
                this.lastGuid = guid;

                // Run task asynchronously for ensuring that there is no another click
                // happened in time interval when double-click can occure.
                Task.Run(async () =>
                {
                    // Wait system double-click time interval.
                    await Task.Delay((int)GetDoubleClickTime());

                    // If no double-click occured in awaited time interval, then
                    // last saved id (saved when first click occured) will be unchanged.
                    if (guid == this.lastGuid)
                    {
                        // Here is any logic for single-click handling.
                        Trace.WriteLine("Single-click occured");
                    }
                });

                return;
            }

            // Can be here only when e.ClickCount > 1, so must change last saved unique id.
            // After that, asynchronously running task (for single-click) will detect
            // that id was changed and so will NOT run single-click logic.
            this.lastGuid = Guid.NewGuid();

            // Here is any logic for double-click handling.
            Trace.WriteLine("Double-click occured");
        }
    }
}

为了测试,在 window 区域点击并跟踪消息写入 visual studio 中的输出 window(菜单视图 -> 输出)。

另一种方法是在双击发生时使用CancellationTokenSource and trigger its Cancel方法。只需替换 lastGuid 字段和 RootElement_OnMouseDown 方法:

private CancellationTokenSource cancellationTokenSource;

private void RootElement_OnMouseDown(object sender, MouseButtonEventArgs e)
    {
        if (e.ClickCount == 1)
        {
            try
            {
                this.cancellationTokenSource = new CancellationTokenSource();
                var token = this.cancellationTokenSource.Token;

                // Run task asynchronously for ensuring that there is no another click
                // happened in time interval when double-click can occure.
                Task.Run(async () =>
                {
                    // Wait system double-click time interval.
                    await Task.Delay((int)GetDoubleClickTime(), token);

                    // Here is any logic for single-click handling.
                    Trace.WriteLine("Single-click occured");
                }, token);
            }
            catch (OperationCanceledException)
            {
                // This exception always occure when task is cancelled.
                // It happening by design, just ignore it.
            }

            return;
        }

        // Cancel single-click task.
        if (this.cancellationTokenSource != null)
        {
            this.cancellationTokenSource.Cancel();
        }

        // Here is any logic for double-click handling.
        Trace.WriteLine("Double-click occured");
    }