双击时,防止单击WPF控件

时间:2022-12-01 10:51:06

I have Image control with PreviewMouseLeftButtonDown event handled. The logic is to change image content when single click occured and to activate other visual style when double click occured.

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

I know about ClickCount property like some answers said (e.g. this) and successfully distinguish between single/double clicks, but problem is that single click occures always, wherether double click follow or not follow the next moment (which is fair enought, anyway). So, with double click both actions processed - for single click and the next moment for double click.

我知道ClickCount属性就像一些答案所说的那样(例如这个)并成功地区分单击/双击,但问题是单击始终发生,其他双击跟随或不跟随下一刻(无论如何都是公平的)。因此,双击两个处理的操作 - 单击,下一刻双击。

The question: is there any method to prevent single click before occuring right after that double click, other than handling this situation with some kind of timers magic?

问题:除了用某种计时器魔法处理这种情况之外,有没有任何方法可以在双击之后立即阻止单击?

Edit:
I found question with good comment, which makes an analogy with windows explorer - how it wait after single click on selected file, and start renaming just ensured that no other click occured after first click.
Delay will definitely exist in purpose to solve this problem, but does it mean that windows explorer using exactly timer, or maybe it have some other option (some property or event that can be awaited) to hold single click in case double click occured?

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

2 个解决方案

#1


5  

Finally there were no suggestions received with timer-unrelated solution (and I didn't find any either), so here is simple example how to prevent single click when double click occurred.

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

Xaml:

<Window x:Class="*.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>

Code-behind:

namespace *.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");
        }
    }
}

For testing, make clicks in window area and track messages writing into output window in visual studio (menu View -> Output).

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

Another way is using CancellationTokenSource and trigger its Cancel method when double-click occured. Just replace lastGuid field and RootElement_OnMouseDown method:

另一种方法是使用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");
    }

#2


4  

I guess you need to use a timer. For getting the max time that is valid for a double click still to occur you could use following function (tested; output is 500 ms):

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

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

(source: how to get the double-click-time in WPF)

(来源:如何在WPF中获得双击时间)

Usually when you have several values you want to bind to one WPF control you use something like ItemsSource and bind it to a List in view model. But I guess that doesn't work for Image control. So you should go with a timer and use the value of the function above for your timer.

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

#1


5  

Finally there were no suggestions received with timer-unrelated solution (and I didn't find any either), so here is simple example how to prevent single click when double click occurred.

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

Xaml:

<Window x:Class="*.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>

Code-behind:

namespace *.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");
        }
    }
}

For testing, make clicks in window area and track messages writing into output window in visual studio (menu View -> Output).

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

Another way is using CancellationTokenSource and trigger its Cancel method when double-click occured. Just replace lastGuid field and RootElement_OnMouseDown method:

另一种方法是使用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");
    }

#2


4  

I guess you need to use a timer. For getting the max time that is valid for a double click still to occur you could use following function (tested; output is 500 ms):

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

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

(source: how to get the double-click-time in WPF)

(来源:如何在WPF中获得双击时间)

Usually when you have several values you want to bind to one WPF control you use something like ItemsSource and bind it to a List in view model. But I guess that doesn't work for Image control. So you should go with a timer and use the value of the function above for your timer.

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