【问题标题】:Prevent single click on WPF control when double click occured双击发生时防止单击 WPF 控件
【发布时间】:2015-11-11 16:06:00
【问题描述】:

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

我知道ClickCount 属性,就像一些答案所说的(e.g. this)并成功区分单击/双击,但问题是单击发生总是,双击是否跟随跟随下一刻(无论如何,这很公平)。因此,通过双击处理两个动作 - 单击一次,下一次双击。

问题:有没有什么方法可以防止在双击之后之前发生单击,除了使用某种计时器魔法来处理这种情况?

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

【问题讨论】:

  • 欢迎来到 StackOverflow!好问题 - 不是我的领域,但希望知道这些东西的人很快就会看到它。
  • @jwg 谢谢!在这里找不到这样的问题时,我感到很惊讶,但是……就是这样:)

标签: c# wpf click double-click


【解决方案1】:

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

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

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

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

【讨论】:

  • 感谢您的信息,我在研究这个问题时根据经验获得了双击时间值 :) 如果计时器解决方案将是一个,我可能会使用 PInvoke,但我仍然希望可以有其他优雅的解决方案,例如操作一些属性或方法...而且我的任务需要完全使用单个 Image 元素,而不是带有图像的列表(顺便说一句,这是其他视觉风格)
  • 是的。我知道这更像是一种解决方法,但我不知道一个优雅的解决方案。希望你能找到一位了解更多的 WPF 架构师:D
【解决方案2】:

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

Xaml:

<Window x:Class="StackOverflow.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 StackOverflow.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");
        }
    }
}

为了进行测试,请在窗口区域中单击并跟踪写入到 Visual Studio 输出窗口中的消息(菜单视图 -> 输出)。

另一种方法是使用CancellationTokenSource 并在发生双击时触发其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");
    }

【讨论】:

    猜你喜欢
    • 2012-06-09
    • 1970-01-01
    • 2017-07-19
    • 2022-10-26
    • 1970-01-01
    • 2010-10-24
    • 2011-03-10
    • 1970-01-01
    • 2019-02-05
    相关资源
    最近更新 更多