防止双击双击命令

鉴于您有一个触发命令的控件:

如果用户双击命令,有没有办法阻止命令被触发两次?

编辑:在这种情况下重要的是我在WPF中使用命令模型。

看来按下按钮时,执行命令。 除了禁用或隐藏按钮之外,我没有看到任何阻止这种情况的方法。

也许第一次点击后按钮应该被禁用,直到处理完成为止?

由vidalsasoon提交的这个问题的检查答案是错误的,并且对于同样问题的所有各种方式都是错误的。

包含需要大量处理时间的代码的任何事件处理程序都可能导致禁用相关按钮的延迟; 无论在处理程序中调用禁用代码行的位置如何。

尝试下面的certificate,您将看到禁用/启用与事件注册无关。 按钮单击事件仍然已注册,仍在处理中。

矛盾certificate1

 private int _count = 0; private void btnStart_Click(object sender, EventArgs e) { btnStart.Enabled = false; _count++; label1.Text = _count.ToString(); while (_count < 10) { btnStart_Click(sender, e); } btnStart.Enabled = true; } 

抗辩certificate2

 private void form1_load(object sender, EventArgs e) { btnTest.Enabled = false; } private void btnStart_Click(object sender, EventArgs e) { btnTest.Enabled = false; btnTest_click(sender, e); btnTest_click(sender, e); btnTest_click(sender, e); btnTest.Enabled = true; } private int _count = 0; private void btnTest_click(object sender, EventArgs e) { _count++; label1.Text = _count.ToString(); } 

简单有效地阻止双击,三击和四击

  private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e) { if (e.ClickCount >= 2) { e.Handled = true; } } 

我有同样的问题,这对我有用:

  

我们像这样解决了…用异步我们找不到任何其他方法来有效地阻止调用此Click的按钮上的额外点击:

 private SemaphoreSlim _lockMoveButton = new SemaphoreSlim(1); private async void btnMove_Click(object sender, RoutedEventArgs e) { var button = sender as Button; if (_lockMoveButton.Wait(0) && button != null) { try { button.IsEnabled = false; } finally { _lockMoveButton.Release(); button.IsEnabled = true; } } } 

假设WPF命令没有给你足够的控制来搞乱点击处理程序,你是否可以在命令处理程序中放置一些代码来记住命令执行的最后一次,如果在给定时间段内请求它则退出? (下面的代码示例)

这个想法是,如果它是双击,你将在几毫秒内收到两次事件,所以忽略第二个事件。

像:(在命令内)

// warning: I haven't tried compiling this, but it should be pretty close DateTime LastInvoked = DateTime.MinDate; Timespan InvokeDelay = Timespan.FromMilliseconds(100); { if(DateTime.Now - LastInvoked <= InvokeDelay) return; // do your work }
// warning: I haven't tried compiling this, but it should be pretty close DateTime LastInvoked = DateTime.MinDate; Timespan InvokeDelay = Timespan.FromMilliseconds(100); { if(DateTime.Now - LastInvoked <= InvokeDelay) return; // do your work } 

(注意:如果它只是一个普通的旧点击处理程序,我会说遵循这个建议: http : //blogs.msdn.com/oldnewthing/archive/2009/04/29/9574643.aspx )

您可以使用EventToCommand中的EventToCommand类来防止这种情况。

处理Click事件并通过EventToCommand将其从您的视图发送到您的viewmodel(您可以使用EventTrigger执行此操作)。
在视图中设置MustToggleIsEnabled="True" ,并在viewmodel中实现CanExecute()方法。
设置CanExecute()以在命令开始执行时返回false,并在命令完成时返回true。

这将在处理命令期间禁用该按钮。

你会认为它就像使用Command一样简单,并且在命令运行时使CanExecute()返回false。 你错了。 即使您明确提出CanExecuteChanged

 public class TestCommand : ICommand { public void Execute(object parameter) { _CanExecute = false; OnCanExecuteChanged(); Thread.Sleep(1000); Console.WriteLine("Executed TestCommand."); _CanExecute = true; OnCanExecuteChanged(); } private bool _CanExecute = true; public bool CanExecute(object parameter) { return _CanExecute; } private void OnCanExecuteChanged() { EventHandler h = CanExecuteChanged; if (h != null) { h(this, EventArgs.Empty); } } public event EventHandler CanExecuteChanged; } 

我怀疑如果这个命令引用了窗口的Dispatcher ,并在调用OnCanExecuteChanged时使用了Invoke ,那么它就可以了。

我可以想到几种方法来解决这个问题。 一个人的JMarsch的方法:简单地跟踪何时调用Execute ,如果在最后几百毫秒内调用它,则不进行任何操作。

更强大的方法可能是让Execute方法启动BackgroundWorker来进行实际处理,让CanExecute返回(!BackgroundWorker.IsBusy) ,并在任务完成时引发CanExecuteChanged 。 一旦Execute()返回,该按钮应该重新查询CanExecute() ,它会立即执行。

你可以设置一个标志

 bool boolClicked = false; button_OnClick { if(!boolClicked) { boolClicked = true; //do something boolClicked = false; } } 

一个简单而优雅的解决方案是在双击方案中创建一个行为禁用反应。 这很容易使用:

   

行为(更多关于行为 – https://www.jayway.com/2013/03/20/behaviors-in-wpf-introduction/

 using System.Windows.Controls; using System.Windows.Input; using System.Windows.Interactivity; public class DisableDoubleClickBehavior : Behavior 

如果您的控件派生自System.Windows.Forms.Control,则可以使用双击事件 。

如果它不是从System.Windows.Forms.Control派生的,那么改为连接mousedown并确认点击次数== 2:

 private void Button_MouseDown(object sender, MouseButtonEventArgs e) { if (e.ClickCount == 2) { //Do stuff } } 

有同样的问题,通过使用附加行为解决它。

 namespace VLEva.Core.Controls { ///  public static class ButtonBehavior { ///  public static readonly DependencyProperty IgnoreDoubleClickProperty = DependencyProperty.RegisterAttached("IgnoreDoubleClick", typeof(bool), typeof(ButtonBehavior), new UIPropertyMetadata(false, OnIgnoreDoubleClickChanged)); ///  public static bool GetIgnoreDoubleClick(Button p_btnButton) { return (bool)p_btnButton.GetValue(IgnoreDoubleClickProperty); } ///  public static void SetIgnoreDoubleClick(Button p_btnButton, bool value) { p_btnButton.SetValue(IgnoreDoubleClickProperty, value); } static void OnIgnoreDoubleClickChanged(DependencyObject p_doDependencyObject, DependencyPropertyChangedEventArgs e) { Button btnButton = p_doDependencyObject as Button; if (btnButton == null) return; if (e.NewValue is bool == false) return; if ((bool)e.NewValue) btnButton.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(btnButton_PreviewMouseLeftButtonDown); else btnButton.PreviewMouseLeftButtonDown -= btnButton_PreviewMouseLeftButtonDown; } static void btnButton_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (e.ClickCount >= 2) e.Handled = true; } } } 

然后通过声明一个样式直接在XAML中将属性设置为TRUE,这样它就可以立即影响所有按钮。 (不要忘记XAML名称空间声明)

  

我正在使用Xamarin和MVVMCross,虽然不是WPF我认为以下解决方案适用,我创建了一个特定于viewmodel的解决方案(不处理特定于平台的UI)我觉得非常方便,使用帮助器或基类对于viewmodel创建一个跟踪命令的List,如下所示:

 private readonly List Commands = new List(); public bool IsCommandRunning(string command) { return Commands.Any(c => c == command); } public void StartCommand(string command) { if (!Commands.Any(c => c == command)) Commands.Add(command); } public void FinishCommand(string command) { if (Commands.Any(c => c == command)) Commands.Remove(command); } public void RemoveAllCommands() { Commands.Clear(); } 

在操作中添加命令,如下所示:

 public IMvxCommand MyCommand { get { return new MvxCommand(async() => { var command = nameof(MyCommand); if (IsCommandRunning(command)) return; try { StartCommand(command); await Task.Delay(3000); //click the button several times while delay } finally { FinishCommand(command); } }); } } 

try / finally只是确保命令始终完成。

通过设置动作异步并执行延迟来测试它,首先点击工作第二个返回条件。

这将检查validation是否已通过,如果已validation,则禁用该按钮。

 private void checkButtonDoubleClick(Button button) { System.Text.StringBuilder sbValid = new System.Text.StringBuilder(); sbValid.Append("if (typeof(Page_ClientValidate) == 'function') { "); sbValid.Append("if (Page_ClientValidate() == false) { return false; }} "); sbValid.Append("this.value = 'Please wait...';"); sbValid.Append("this.disabled = true;"); sbValid.Append(this.Page.ClientScript.GetPostBackEventReference(button, "")); sbValid.Append(";"); button.Attributes.Add("onclick", sbValid.ToString()); }