鉴于您有一个触发命令的控件:
<Button Command="New"/>
如果用户双击命令,有没有办法防止命令被触发两次?
编辑:在这种情况下,重要的是我在WPF中使用Commanding模型。
似乎只要按下按钮,就会执行命令。除了禁用或隐藏按钮之外,我没有看到任何阻止这种情况的方法。
答案 0 :(得分:16)
由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();
}
答案 1 :(得分:10)
我有同样的问题,这对我有用:
<Button>
<Button.InputBindings>
<MouseBinding Gesture="LeftClick" Command="New" />
</Button.InputBindings>
</Button>
答案 2 :(得分:10)
简单&amp;有效阻止双击,三击和四击
<Button PreviewMouseDown="Button_PreviewMouseDown"/>
private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount >= 2)
{
e.Handled = true;
}
}
答案 3 :(得分:5)
我们这样解决了...使用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 :(得分:4)
假设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)
答案 5 :(得分:4)
您可以使用MVVMLightToolkit中的EventToCommand
课程来防止这种情况发生。
处理点击事件并通过EventToCommand
从您的视图发送到您的视图模型(您可以使用EventTrigger
执行此操作)。
在视图中设置MustToggleIsEnabled="True"
,并在您的视图模型中实施CanExecute()
方法
设置CanExecute()
以在命令开始执行时返回false,并在命令完成时返回true。
这将在处理命令期间禁用该按钮。
答案 6 :(得分:4)
您认为在命令运行时使用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
返回(!BackgroundWorker.IsBusy)
,然后提升{{1}在任务完成时。只要CanExecuteChanged
返回,该按钮就会重新查询CanExecute()
,这会立即执行。
答案 7 :(得分:3)
你可以设置一个标志
bool boolClicked = false;
button_OnClick
{
if(!boolClicked)
{
boolClicked = true;
//do something
boolClicked = false;
}
}
答案 8 :(得分:2)
一个简单而优雅的解决方案是在双击方案中在第二次单击时创建一个行为禁用反应。这很容易使用:
$scope.savePdf = function(){
html2canvas($("#graphRender"), {
onrendered: function(canvas) {
console.log(canvas);
var imgData = canvas.toDataURL(
'image/png');
var doc = new jsPDF('p', 'mm');
doc.addImage(imgData, 'PNG', 1000, 1000);
doc.save('sample-file.pdf');
}
});
}
<Button Command="New">
<i:Interaction.Behaviors>
<behaviors:DisableDoubleClickBehavior />
</i:Interaction.Behaviors>
</Button>
答案 9 :(得分:0)
如果您的控件派生自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
}
}
答案 10 :(得分:0)
有同样的问题,通过使用附加行为来解决它。
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>
答案 11 :(得分:0)
我正在使用Xamarin和MVVMCross,尽管不是WPF,但我认为以下解决方案适用,我创建了一个解决方案,该解决方案特定于viewmodel(不处理特定于平台的UI),我认为它很方便,使用了一个助手或viewmodel的基类创建一个跟踪命令的List,如下所示:
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仅确保命令始终完成。
通过将动作设置为异步并进行延迟来对其进行测试,第一次点击会起作用,第二次会在条件返回。
答案 12 :(得分:0)
将代码包装在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是UserControl或Window绑定到的属性
<Window
Cursor = {Binding Path=CursorType}>
答案 13 :(得分:0)
这里唯一真正的解决方案是创建一个使用 ConcurrentQueue 命令的 CommandHandler 单例类。命令处理程序需要它自己的处理循环,该循环在第一次按下按钮后启动并在队列为空时结束,这需要在它自己的线程中运行。
每个点击处理程序然后将命令推送到此队列,然后执行命令。如果相同的命令在队列中出现两次,您可以简单地忽略处理它(或做其他事情)
我在这个问题中看到的所有其他内容都不起作用,因为它们使用非原子操作来检查按钮是否已快速连续按下两次。这可能会失败,因为您可以在设置布尔值/计时器/信号量之前获得双重入口。
答案 14 :(得分:0)
我的按钮绑定到一个可以触发 Run() 的委托函数:
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;
}
}
答案 15 :(得分:-2)
检查验证是否已通过,如果验证,则禁用该按钮。
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());
}