C# 防止双击重复触发命令

C# 防止双击重复触发命令,c#,.net,wpf,double-click,C#,.net,Wpf,Double Click,假设您有一个触发命令的控件: <Button Command="New"/> 如果用户双击命令,是否有方法防止命令被触发两次 编辑:在这种情况下,重要的是我在WPF中使用模型 无论何时按下按钮,都会执行该命令。除了禁用或隐藏按钮之外,我看不到任何防止这种情况发生的方法。您可以设置一个标志 bool boolClicked = false; button_OnClick { if(!boolClicked) { boolClicked = true

假设您有一个触发命令的控件:

<Button Command="New"/>

如果用户双击命令,是否有方法防止命令被触发两次

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

无论何时按下按钮,都会执行该命令。除了禁用或隐藏按钮之外,我看不到任何防止这种情况发生的方法。

您可以设置一个标志

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

如果控件派生自System.Windows.Forms.control,则可以使用

如果它不是从System.Windows.Forms.Control派生的,则改为单击计数==2并确认:

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

假设WPF命令没有给您足够的控制权来处理click处理程序,您是否可以在命令处理程序中放入一些代码,以记住上次执行命令的时间,并在给定时间段内请求该命令时退出?(下面的代码示例)

如果是双击,您将在毫秒内收到两次事件,因此忽略第二个事件

类似:(命令内部)


//警告:我还没有试过编译这个,但应该非常接近
DateTime LastInvoked=DateTime.MinDate;
Timespan InvokedLay=Timespan.From毫秒(100);
{

if(DateTime.Now-LastInvoked检查验证是否通过,如果通过,则禁用按钮

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());
    }

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

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


这将在处理命令的过程中禁用按钮。

我也有同样的问题,这对我有效:

<Button>
    <Button.InputBindings>
            <MouseBinding Gesture="LeftClick" Command="New" />
    </Button.InputBindings>
</Button>

您可能会认为,在命令运行时使用
命令并使
CanExecute()
返回false非常简单。您可能会错。即使您明确提出
canexecutechange

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;
}
我怀疑,如果此命令引用了窗口的
调度程序
,并在调用
oncanexecutechange
时使用了
Invoke
,那么它就可以工作了

我可以想出两种方法来解决这个问题。一种是JMarsch的方法:只需跟踪
Execute
何时被调用,如果在最后几百毫秒内被调用,则不做任何操作即可退出


更可靠的方法可能是让
Execute
方法启动
BackgroundWorker
来执行实际处理,让
CanExecute
返回
(!BackgroundWorker.IsBusy)
,并在任务完成时提升
CanExecuteChanged
。按钮应重新查询
CanExecute()
只要
Execute()
返回,它就会立即执行。

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

尝试下面的证明,您将看到禁用/启用与事件注册没有关联。按钮单击事件仍然被注册并被处理

矛盾证明1

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;
}

简单有效地阻止两次、三次和四次点击

<Button PreviewMouseDown="Button_PreviewMouseDown"/>

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

私有无效按钮\u预览鼠标向下(对象发送器,鼠标按钮Ventargs e)
{
如果(例如,单击计数>=2)
{
e、 已处理=正确;
}
}

我们是这样解决的……使用async,我们找不到任何其他方法来有效阻止对调用此单击的按钮的额外单击:

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;
        }
    }
}

一个简单而优雅的解决方案是在双击场景中,在第二次单击时创建禁用行为的反应。这非常容易使用:

  <Button Command="New">
          <i:Interaction.Behaviors>
            <behaviors:DisableDoubleClickBehavior />
          </i:Interaction.Behaviors>
  </Button>

行为(更多关于行为-)

使用System.Windows.Controls;
使用System.Windows.Input;
使用System.Windows.Interactive;
公共类禁用DoubleClickBehavior:行为
{
受保护的覆盖无效附加()
{
base.onatached();
AssociatedObject.PreviewMouseDoubleClick+=AssociatedObjectOnPreviewMouseDoubleClick;
}
private void Associated对象预览鼠标双击(对象发送者,鼠标按钮文本目标鼠标按钮文本目标)
{
mouseButtonEventArgs.Handled=true;
}
附加时受保护的覆盖无效()
{
AssociatedObject.PreviewMouseDoubleClick-=AssociatedObjectOnPreviewMouseDoubleClick;
base.OnDetaching();
}
}

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

namespace VLEva.Core.Controls
{
    /// <summary></summary>
    public static class ButtonBehavior
    {
        /// <summary></summary>
        public static readonly DependencyProperty IgnoreDoubleClickProperty = DependencyProperty.RegisterAttached("IgnoreDoubleClick",
                                                                                                                  typeof(bool),
                                                                                                                  typeof(ButtonBehavior),
                                                                                                                  new UIPropertyMetadata(false, OnIgnoreDoubleClickChanged));

        /// <summary></summary>
        public static bool GetIgnoreDoubleClick(Button p_btnButton)
        {
            return (bool)p_btnButton.GetValue(IgnoreDoubleClickProperty);
        }

        /// <summary></summary>
        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;
        }

    }
}
名称空间VLEva.Core.Controls
{
/// 
公共静态类按钮行为
{
/// 
公共静态只读DependencyProperty IgnoreDoubleClickProperty=DependencyProperty.RegisterAttached(“IgnoreDoubleClick”,
类型(bool),
类型(按钮行为),
新的UIPropertyMetadata(假,OnIgnoreDoubleClickChanged);
/// 
公共静态bool GetIgnoreDoubleClick(按钮p_btn按钮)
{
返回(bool)p_btnButton.GetValue(忽略DoubleClickProperty);
}
/// 
公共静态无效设置忽略双击(按钮p_btnButto
  <Button Command="New">
          <i:Interaction.Behaviors>
            <behaviors:DisableDoubleClickBehavior />
          </i:Interaction.Behaviors>
  </Button>
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;

public class DisableDoubleClickBehavior : Behavior<Button>
{
    protected override void OnAttached()
    {
        base.OnAttached();
        AssociatedObject.PreviewMouseDoubleClick += AssociatedObjectOnPreviewMouseDoubleClick;
    }

    private void AssociatedObjectOnPreviewMouseDoubleClick(object sender, MouseButtonEventArgs mouseButtonEventArgs)
    {
        mouseButtonEventArgs.Handled = true;
    }

    protected override void OnDetaching()
    {
        AssociatedObject.PreviewMouseDoubleClick -= AssociatedObjectOnPreviewMouseDoubleClick;
        base.OnDetaching();
    }
}
namespace VLEva.Core.Controls
{
    /// <summary></summary>
    public static class ButtonBehavior
    {
        /// <summary></summary>
        public static readonly DependencyProperty IgnoreDoubleClickProperty = DependencyProperty.RegisterAttached("IgnoreDoubleClick",
                                                                                                                  typeof(bool),
                                                                                                                  typeof(ButtonBehavior),
                                                                                                                  new UIPropertyMetadata(false, OnIgnoreDoubleClickChanged));

        /// <summary></summary>
        public static bool GetIgnoreDoubleClick(Button p_btnButton)
        {
            return (bool)p_btnButton.GetValue(IgnoreDoubleClickProperty);
        }

        /// <summary></summary>
        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;
        }

    }
}
<Style x:Key="styleBoutonPuff" TargetType="{x:Type Button}">
    <Setter Property="VLEvaControls:ButtonBehavior.IgnoreDoubleClick" Value="True" />
    <Setter Property="Cursor" Value="Hand" />
</Style>
private readonly List<string> Commands = new List<string>();

        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);
                    }
                });
            }
        }
    private Cursor _CursorType;
    // Property to set and get the cursor type
    public Cursor CursorType
    {
      get {return _CursorType; }
      set
      {
        _CursorType = value;
        OnPropertyChanged("CursorType");
      }
    }


    private void ExecutedMethodOnButtonPress()
    {
       try
       {
         CursorType = Cursors.Wait;
         // Run all other code here
       }
       finally
       {
         CursorType = Cursors.Arrow;
       }
    }
<Window 
Cursor = {Binding Path=CursorType}>
private const int BUTTON_EVENT_DELAY_MS = 1000; //1 second. Setting this time too quick may allow double and triple clicking if the user is quick.
private bool runIsRunning = false;

private void Run()
{
    try
    {
        if (runIsRunning) //Prevent Double and Triple Clicking etc. We just want to Run run once until its done!
        {
            return;
        }
        runIsRunning = true;

        EventAggregator.GetEvent<MyMsgEvent>().Publish("my string");

        Thread.Sleep(BUTTON_EVENT_DELAY_MS);  
        runIsRunning = false;
    }
    catch  //catch all to reset runIsRunning- this should never happen.
    {
        runIsRunning = false;
    }
}