在WPF中,按钮命令触发正常是立即触发;但有时我们需要防止无意按下而误触发;那么如何按住按钮一段时间,比如1s,或者500ms,然后触发绑定命令。
我希望它能像行为一样工作,只需在 Xaml 中配置按钮的持续时间。
您可以使用
Task.Delay
和任务取消来延迟按钮操作。
要真正消除按钮的抖动(将一段时间内的多次点击解释为单次点击),您必须使用基于计时器的实现。但从你的问题来看,你似乎只是想要一个简单的延迟。
从用户体验的角度来看,延迟按钮点击会显着降低用户体验。这不是用户期望的按钮行为。而且,您必须按住按钮未知的时间才能调用该操作,这并不直观。我认为这是一种 UI 设计味道。
如果该按钮将调用关键操作,您应该考虑显示一个确认对话框(“您确定吗?”-“确定”或“取消”)。然后取消此对话框可以恢复原始值(或简单地吞下按钮操作)。
我认为这比使用延迟按钮要好得多(就用户体验而言)。
按钮是“点击即忘”的。有一些众所周知的例外情况,其中按住按钮(例如
RepeatButton
)会连续增加例如日期值。在所有其他场景中,按钮必须在单击时执行,其中按下状态的持续时间应该无关紧要。
以下示例创建一个自定义
DelayButton
,它支持所有单击模式(按下、释放、悬停)并延迟 Button.Click
事件和 Button.Command
调用:
<!-- Use DelayInMilliseconds="0" to use it as a normal button -->
<DelayButton DelayInMilliseconds="1000" />
public class DelayButton : Button
{
public int DelayInMilliseconds
{
get => (int)GetValue(DelayInMillisecondsProperty);
set => SetValue(DelayInMillisecondsProperty, value);
}
public static readonly DependencyProperty DelayInMillisecondsProperty = DependencyProperty.Register(
nameof(DelayInMilliseconds),
typeof(int),
typeof(DelayButton),
new PropertyMetadata(0));
private CancellationTokenSource? delayCancellationTokenSource;
private bool isClickValid;
protected override async void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
try
{
this.delayCancellationTokenSource = new CancellationTokenSource();
await Task.Delay(TimeSpan.FromMilliseconds(this.DelayInMilliseconds), this.delayCancellationTokenSource.Token);
if (e.LeftButton is MouseButtonState.Pressed)
{
this.isClickValid = true;
base.OnMouseLeftButtonDown(e);
}
}
catch (OperationCanceledException)
{
return;
}
finally
{
this.delayCancellationTokenSource?.Dispose();
this.delayCancellationTokenSource = null;
}
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
this.delayCancellationTokenSource?.Cancel();
if (this.ClickMode is ClickMode.Release)
{
if (this.isClickValid)
{
base.OnMouseLeftButtonUp(e);
}
}
else
{
base.OnMouseLeftButtonUp(e);
}
this.isClickValid = false;
}
protected override async void OnMouseEnter(MouseEventArgs e)
{
try
{
if (this.ClickMode is ClickMode.Hover)
{
this.delayCancellationTokenSource = new CancellationTokenSource();
await Task.Delay(TimeSpan.FromMilliseconds(this.DelayInMilliseconds), this.delayCancellationTokenSource.Token);
}
base.OnMouseEnter(e);
}
catch (OperationCanceledException)
{
return;
}
finally
{
this.delayCancellationTokenSource?.Dispose();
this.delayCancellationTokenSource = null;
}
}
protected override void OnMouseLeave(MouseEventArgs e)
{
this.delayCancellationTokenSource?.Cancel();
base.OnMouseLeave(e);
}
}