Запретить двойной щелчок по двойному стрельбе командой
Учитывая, что у вас есть элемент управления, который запускает команду:
<Button Command="New"/>
Есть ли способ предотвратить запуск команды дважды, если пользователь дважды нажимает на команду?
EDIT: В этом случае важно то, что я использую Commanding в WPF. p >
Кажется, что всякий раз, когда нажимается кнопка, выполняется команда. Я не вижу никакого способа предотвратить это, кроме отключения или скрытия кнопки.
Проверенный ответ на этот вопрос, представленный vidalsasoon, неверен, и это неверно для всех различных способов, заданных этим же вопросом.
Возможно, что любой обработчик событий, содержащий код, требующий значительного времени процесса, может привести к задержке отключения кнопки на вопрос; независимо от того, где в обработчике вызывается отключающая строка кода.
Попробуйте приведенные ниже доказательства, и вы увидите, что disable/enable не коррелирует с регистрацией событий. Событие нажатия кнопки по-прежнему регистрируется и все еще обрабатывается.
Доказательство противоречивости 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();
}
У меня была такая же проблема, и это сработало для меня:
<Button>
<Button.InputBindings>
<MouseBinding Gesture="LeftClick" Command="New" />
</Button.InputBindings>
</Button>
Простой и эффективный для блокировки двойных, тройных и четырехкратных кликов
<Button PreviewMouseDown="Button_PreviewMouseDown"/>
private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount >= 2)
{
e.Handled = true;
}
}
Вы думаете, что это будет так же просто, как использовать Command
и сделать CanExecute()
return 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
запустить a BackgroundWorker
для выполнения фактической обработки, иметь CanExecute
return (!BackgroundWorker.IsBusy)
и поднять CanExecuteChanged
, когда задача будет завершена. Кнопка должна запрашивать CanExecute()
, как только возвращается Execute()
, что будет сделано мгновенно.
Предполагая, что командование 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
}
(обратите внимание: если бы это был простой простой обработчик, я бы сказал следующее:
http://blogs.msdn.com/oldnewthing/archive/2009/04/29/9574643.aspx)
Вы можете использовать класс EventToCommand
в MVVMLightToolkit, чтобы предотвратить это.
Обработайте событие Click и отправьте его через EventToCommand
из вашего представления в вашу модель просмотра (вы можете использовать EventTrigger
для этого).
Задайте MustToggleIsEnabled="True"
в своем представлении и реализуйте метод CanExecute()
в вашей модели viewmodel.
Установите CanExecute()
, чтобы вернуть значение false, когда команда начинает выполняться, и вернется к истине, когда команда выполнена.
Это отключит кнопку на время обработки команды.
Вы можете установить флаг
bool boolClicked = false;
button_OnClick
{
if(!boolClicked)
{
boolClicked = true;
//do something
boolClicked = false;
}
}
Мы решили это так... с помощью 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>
Поведение (подробнее о поведении - 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();
}
}
Возможно, кнопка должна быть отключена после первого щелчка и до завершения обработки?
Если ваш элемент управления происходит из 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
{
/// <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;
}
}
}
а затем просто установите свойство TRUE либо непосредственно в XAML, объявив стиль, чтобы он мог воздействовать на все ваши кнопки одновременно. (не забудьте объявление пространства имен XAML)
<Style x:Key="styleBoutonPuff" TargetType="{x:Type Button}">
<Setter Property="VLEvaControls:ButtonBehavior.IgnoreDoubleClick" Value="True" />
<Setter Property="Cursor" Value="Hand" />
</Style>
Это проверяет, прошла ли проверка, и, если это произойдет, отключит кнопку.
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());
}