Предотвратить двойной щелчок от двойного запуска команды - PullRequest
12 голосов
/ 08 мая 2009

Учитывая, что у вас есть элемент управления, который запускает команду:

<Button Command="New"/>

Есть ли способ предотвратить двойной запуск команды, если пользователь дважды щелкнет по команде?

РЕДАКТИРОВАТЬ: В данном случае важно то, что я использую модель Commanding в WPF.

Похоже, что при каждом нажатии кнопки команда выполняется. Я не вижу способа предотвратить это, кроме отключения или скрытия кнопки.

Ответы [ 14 ]

16 голосов
/ 07 мая 2013

Проверенный ответ на этот вопрос, представленный vidalsasoon, является неправильным и неправильным для всех различных способов, которыми задавался этот же вопрос. Возможно, что любой обработчик событий, содержащий код, требующий значительного времени обработки, может привести к задержке отключения рассматриваемой кнопки; независимо от того, где в обработчике вызывается отключающая строка кода.

Попробуйте приведенные ниже доказательства, и вы увидите, что отключение / включение не имеет отношения к регистрации событий. Событие нажатия кнопки все еще зарегистрировано и все еще обработано.

Доказательство от противоречия 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();
}
10 голосов
/ 02 апреля 2014

Простой и эффективный для блокировки двойных, тройных и четырехкратных кликов

<Button PreviewMouseDown="Button_PreviewMouseDown"/>

private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
    if (e.ClickCount >= 2)
    {
        e.Handled = true;
    }
}
10 голосов
/ 26 августа 2011

У меня была такая же проблема, и у меня это сработало:

<Button>
    <Button.InputBindings>
            <MouseBinding Gesture="LeftClick" Command="New" />
    </Button.InputBindings>
</Button>
5 голосов
/ 30 сентября 2015

Мы решили это следующим образом ... с помощью async мы не смогли найти какой-либо другой способ эффективно блокировать дополнительные щелчки на кнопке, которая вызывает этот 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;
        }
    }
}
4 голосов
/ 26 августа 2011

Можно подумать, что это будет так же просто, как использовать 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, она бы работала.

Я могу придумать пару способов решить эту проблему. Один из подходов JMarsch: просто отслеживать, когда вызывается Execute, и спасать, ничего не делая, если он вызывался в последние несколько сотен миллисекунд.

Более надежный способ может состоять в том, чтобы метод Execute запускал BackgroundWorker для выполнения фактической обработки, имел CanExecute return (!BackgroundWorker.IsBusy) и повышал CanExecuteChanged, когда задача завершена. Кнопка должна запросить CanExecute(), как только вернется Execute(), что она сделает мгновенно.

4 голосов
/ 22 марта 2011

Вы можете использовать класс EventToCommand в MVVMLightToolkit , чтобы предотвратить это.

Обработайте событие Click и отправьте его через EventToCommand из вашего представления в вашу модель представления (вы можете использовать EventTrigger для этого).
Установите MustToggleIsEnabled="True" в вашем представлении и реализуйте метод CanExecute() в вашей модели представления.
Установите CanExecute(), чтобы возвращать значение false, когда команда начинает выполняться, и возвращать значение true, когда команда выполнена.

Это отключит кнопку на время обработки команды.

4 голосов
/ 09 мая 2009

Предполагая, что WPF Commanding не дает вам достаточного контроля, чтобы связываться с обработчиком щелчков, вы можете поместить некоторый код в обработчик команд, который запоминает последний раз, когда команда была выполнена, и завершается, если она запрашивается в течение заданного периода времени ? (пример кода ниже)

Идея состоит в том, что если это двойной щелчок, вы получите событие дважды в течение миллисекунд, поэтому игнорируйте второе событие.

Что-то вроде: (внутри команды)


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

3 голосов
/ 08 мая 2009

Вы можете установить флаг

bool boolClicked = false;
button_OnClick
{
    if(!boolClicked)
    {
        boolClicked = true;
        //do something
        boolClicked = false;
    }
}
2 голосов
/ 03 ноября 2016

Простое и элегантное решение - создать реакцию отключения поведения при втором щелчке в сценарии с двойным щелчком. Это довольно легко использовать:

  <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();
    }
}
0 голосов
/ 09 ноября 2018

Оберните код в блок try-catch-finally или try-finally. Оператор 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 - это свойство, с которым UserControl или Window связан

<Window 
Cursor = {Binding Path=CursorType}>
...