Запретить двойной щелчок по двойному стрельбе командой

143
6

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


<Button Command="New"/>

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


EDIT: В этом случае важно то, что я использую Commanding в WPF. p >

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

спросил(а) 2021-01-19T14:05:08+03:00 9 месяцев назад
1
Решение
177

Проверенный ответ на этот вопрос, представленный 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();
}

ответил(а) 2021-01-19T14:05:08+03:00 9 месяцев назад
129

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


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

ответил(а) 2021-01-19T14:05:08+03:00 9 месяцев назад
112

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


<Button PreviewMouseDown="Button_PreviewMouseDown"/>

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

ответил(а) 2021-01-19T14:05:08+03:00 9 месяцев назад
102

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

ответил(а) 2021-01-19T14:05:08+03:00 9 месяцев назад
92

Предполагая, что командование 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)

ответил(а) 2021-01-19T14:05:08+03:00 9 месяцев назад
92

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


Обработайте событие Click и отправьте его через EventToCommand из вашего представления в вашу модель просмотра (вы можете использовать EventTrigger для этого).

Задайте MustToggleIsEnabled="True" в своем представлении и реализуйте метод CanExecute() в вашей модели viewmodel.

Установите CanExecute(), чтобы вернуть значение false, когда команда начинает выполняться, и вернется к истине, когда команда выполнена.


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

ответил(а) 2021-01-19T14:05:08+03:00 9 месяцев назад
92

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


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

ответил(а) 2021-01-19T14:05:08+03:00 9 месяцев назад
79

Мы решили это так... с помощью 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;
}
}
}

ответил(а) 2021-01-19T14:05:08+03:00 9 месяцев назад
79

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


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

ответил(а) 2021-01-19T14:05:08+03:00 9 месяцев назад
65

Возможно, кнопка должна быть отключена после первого щелчка и до завершения обработки?

ответил(а) 2021-01-19T14:05:08+03:00 9 месяцев назад
46

Если ваш элемент управления происходит из System.Windows.Forms.Control, вы можете использовать событие двойного щелчка.


Если это не происходит из System.Windows.Forms.Control, тогда подключить mousedown вместо подтверждения кол-ва счетчика == 2:


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

ответил(а) 2021-01-19T14:05:08+03:00 9 месяцев назад
47

Имел ту же проблему, разрешил ее, используя приложенное поведение.


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>

ответил(а) 2021-01-19T14:05:08+03:00 9 месяцев назад
-7

Это проверяет, прошла ли проверка, и, если это произойдет, отключит кнопку.


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

ответил(а) 2021-01-19T14:05:08+03:00 9 месяцев назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема