代码之家  ›  专栏  ›  技术社区  ›  Josh G

防止双击触发命令

  •  11
  • Josh G  · 技术社区  · 16 年前

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

    <Button Command="New"/>
    

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

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

    每当按下按钮,命令就会执行。除了禁用或隐藏按钮之外,我看不到任何方法来阻止这种情况。

    15 回复  |  直到 6 年前
        1
  •  0
  •   vidalsasoon    16 年前

    也许应该在第一次单击之后禁用该按钮,直到处理完成?

        2
  •  16
  •   Code Maverick Ashish Gupta    11 年前

    Vidalsason提交的这个问题的经检查的答案是错误的,并且这个问题的所有方法都是错误的。

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

    尝试下面的证明,您将看到禁用/启用与事件注册没有关联。Button Click事件仍在注册中,并且仍在处理中。

    矛盾证明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;
    
    }
    

    相反证明2

    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();
    }
    
        3
  •  10
  •   Jon Dosmann    11 年前

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

    <Button PreviewMouseDown="Button_PreviewMouseDown"/>
    
    private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
    {
        if (e.ClickCount >= 2)
        {
            e.Handled = true;
        }
    }
    
        4
  •  9
  •   Olek    13 年前

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

    <Button>
        <Button.InputBindings>
                <MouseBinding Gesture="LeftClick" Command="New" />
        </Button.InputBindings>
    </Button>
    
        5
  •  5
  •   John Fairbanks    9 年前

    我们是这样解决的…使用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;
            }
        }
    }
    
        6
  •  4
  •   JMarsch    16 年前

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

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

    比如:(在命令里面)

    
    // 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 )

        7
  •  4
  •   stombeur    14 年前

    你可以使用 EventToCommand 类中 MVVMLightToolkit 为了防止这种情况。

    处理Click事件并发送给 事件命令 从视图到视图模型(可以使用 EventTrigger 这样做)。
    集合 MustToggleIsEnabled="True" 在您的视图中并实现 CanExecute() 方法。
    集合 可执行() 当命令开始执行时返回false,当命令完成时返回true。

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

        8
  •  4
  •   Robert Rossney    13 年前

    你会认为这就像使用 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 并使用 Invoke 当它被召唤 OnCanExecuteChanged 它会起作用的。

    我可以想出一些方法来解决这个问题。一个人的方法:简单地跟踪 Execute 如果在过去的几百毫秒内被调用,则不做任何操作就被调用。

    一个更强大的方法可能是 执行 方法启动 BackgroundWorker 要进行实际处理,必须 CanExecute 返回 (!BackgroundWorker.IsBusy) 并提出 已更改CanExecuteChanged 任务完成时。该按钮应重新查询 可执行() 一旦 Execute() 返回,它将立即执行。

        9
  •  3
  •   Nathan Koop    16 年前

    你可以设置一个标志

    bool boolClicked = false;
    button_OnClick
    {
        if(!boolClicked)
        {
            boolClicked = true;
            //do something
            boolClicked = false;
        }
    }
    
        10
  •  2
  •   mikka    8 年前

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

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

    行为(有关行为的详细信息- 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<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();
        }
    }
    
        11
  •  0
  •   Noel Kennedy    16 年前

    如果控件派生自System.Windows.Forms.Control,则可以使用 double click event .

    如果它不是从System.Windows.Forms.Control派生的,则 wire up mousedown 相反,确认单击计数==2:

    private void Button_MouseDown(object sender, MouseButtonEventArgs e)
    {
        if (e.ClickCount == 2)
        {
           //Do stuff
        }
     }
    
        12
  •  0
  •   Sam L.    8 年前

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

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

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

    <Style x:Key="styleBoutonPuff" TargetType="{x:Type Button}">
        <Setter Property="VLEvaControls:ButtonBehavior.IgnoreDoubleClick" Value="True" />
        <Setter Property="Cursor" Value="Hand" />
    </Style>
    
        13
  •  0
  •   JpSiller    6 年前

    我正在使用xamarin和mvmcross,虽然不是wpf,但我认为以下解决方案适用,我创建了一个特定于viewModel(不处理特定于平台的UI)的解决方案,我认为这非常方便,使用viewModel的助手或基类创建一个跟踪命令的列表,如下所示:

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

    Try/Finally只是确保命令始终完成。

    通过设置动作异步并进行延迟测试,第一个tap工作,第二个tap返回条件。

        14
  •  0
  •   javacoder123    6 年前

    在try catch finally或try finally块中包装代码。无论try中发生什么错误,都将始终调用finally语句。

    例子

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

    注意:CursorType是用户控件或窗口绑定到的属性

    <Window 
    Cursor = {Binding Path=CursorType}>
    
        15
  •  -2
  •   ta4ka    15 年前

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

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