如何按下按钮并按下按钮, 然后在 WPF 中触发绑定命令
原标题:How to press and hold the button with some time then to trigger the binding command in WPF
In WPF,the button command trigger normal is immediately;but sometime we need Prevent False Triggering by Unintentionally pressed;So how to Press and hold the button with some time,such as 1s,or 500ms,then trigger the binding command.
I hope it will work like behavior,just in Xaml to config the The duration time of the button.
Thank you very much, BionicCode! Inspired by you, I change a litte then it work well:
public class DelayButton : Button
{
///
/// Delay Milliseconds
///
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));
///
/// Delay Command
///
public ICommand DelayCommand
{
get { return (ICommand)GetValue(DelayCommandProperty); }
set { SetValue(DelayCommandProperty, value); }
}
public static readonly DependencyProperty DelayCommandProperty = DependencyProperty.Register("DelayCommand",
typeof(ICommand),
typeof(DelayButton),
new PropertyMetadata(null));
///
/// cts
///
private CancellationTokenSource? delayCancellationTokenSource;
///
/// Trigger flag
///
private bool isClickValid = false;
///
/// Mouse Down Event
///
///
protected override async void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
if (DelayInMilliseconds <= 0)
{
//One press,one trigger
this.isClickValid = true;
//Execute DelayCommand
DelayCommand?.Execute(this);
return;
}
try
{
this.delayCancellationTokenSource = new CancellationTokenSource();
await Task.Delay(TimeSpan.FromMilliseconds(this.DelayInMilliseconds), this.delayCancellationTokenSource.Token);
//cheack mouse and not triggr
if (e.LeftButton is MouseButtonState.Pressed && !this.isClickValid)
{
//One press,one trigger
this.isClickValid = true;
//Execute DelayCommand
DelayCommand?.Execute(this);
}
}
catch (OperationCanceledException)
{
return;
}
finally
{
this.delayCancellationTokenSource?.Dispose();
this.delayCancellationTokenSource = null;
}
}
///
/// Mouse Up Event
///
///
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
if (!this.isClickValid)
{
//time not enough
this.delayCancellationTokenSource?.Cancel();
}
else
{
//time enough,For Next
this.isClickValid = false;
}
}
///
/// Mouse Leave Event
///
///
protected override void OnMouseLeave(MouseEventArgs e)
{
this.delayCancellationTokenSource?.Cancel();
}
}
最佳回答
You can use Task.Delay and task cancellation to delay the button actions.
To actually debounce the button (interpret multiple clicks within a time span as a single click) you would have to remove the cancellation part and guard the event handlers against reentrancy (or use a timer that can only be started once during the debounce period). But from your question it appears you just want a simple delay.
From a UX point of view delaying a button click significantly degrades the user experience. It s not the behavior the user would expect from a button. And it s not intuitive to figure out that you must keep the button pressed for an unknown amount of time to invoke the action. I would consider this a UI design smell.
If the button will invoke critical actions, you should consider showing a confirmation dialog ("Are you sure?"- "Ok" or "Cancel"). Then cancelling this dialog could restore the original values (or simply swallow the button action).
I think that this is far better flow (in terms of UX) than using a delayed button.
Buttons are click-and-forget. There are well-known exceptions, where holding the button (e.g. RepeatButton) would continuously increment an e.g. date value. Every other scenario a button must execute on click where the duration of the pressed state should be irrelevant.
The following example creates a custom DelayButton that supports all click modes (press, release, hover) as well as keyboard input (space and enter) and that delays the Button.Click event and the Button.Command invocation.
It also shows a visual feedback to give the user a cue for how long he has to press the button (a bar will "charge" to fill the button and turns from red to green once the delay has elapsed and the action commenced:
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, OnDelayInMillisecondsChanged));
private static void OnDelayInMillisecondsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
=> ((DelayButton)d).UpdateeProgressAnimation();
public SolidColorBrush ProgressBrush
{
get => (SolidColorBrush)GetValue(ProgressBrushProperty);
set => SetValue(ProgressBrushProperty, value);
}
public static readonly DependencyProperty ProgressBrushProperty = DependencyProperty.Register(
nameof(ProgressBrush),
typeof(SolidColorBrush),
typeof(DelayButton),
new PropertyMetadata(Brushes.DarkRed, OnProgressBrushChanged));
private static void OnProgressBrushChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
=> ((DelayButton)d).UpdateeProgressAnimation();
private CancellationTokenSource? delayCancellationTokenSource;
private bool isClickValid;
private bool isExecutingKeyAction;
private ProgressBar part_ProgressBar;
private Storyboard progressStoryBoard;
static DelayButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(DelayButton), new FrameworkPropertyMetadata(typeof(DelayButton)));
}
public DelayButton()
{
this.ClickMode = ClickMode.Press;
this.progressStoryBoard = new Storyboard()
{
FillBehavior = FillBehavior.HoldEnd,
};
UpdateeProgressAnimation();
}
private void UpdateeProgressAnimation()
{
if (this.progressStoryBoard.IsFrozen)
{
this.progressStoryBoard = this.progressStoryBoard.Clone();
}
var delayDuration = TimeSpan.FromMilliseconds(this.DelayInMilliseconds);
var progressAnimation = new DoubleAnimation(0, 100, new Duration(delayDuration), FillBehavior.HoldEnd);
Storyboard.SetTargetProperty(progressAnimation, new PropertyPath(ProgressBar.ValueProperty));
this.progressStoryBoard.Children.Add(progressAnimation);
var colorAnimation = new ColorAnimation(this.ProgressBrush.Color, Colors.Green, new Duration(delayDuration), FillBehavior.HoldEnd);
Storyboard.SetTargetProperty(colorAnimation, new PropertyPath("(0).(1)", Control.ForegroundProperty, SolidColorBrush.ColorProperty));
this.progressStoryBoard.Children.Add(colorAnimation);
this.progressStoryBoard.Freeze();
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.part_ProgressBar = GetTemplateChild("PART_ProgressBar") as ProgressBar;
}
protected override async void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
try
{
this.delayCancellationTokenSource = new CancellationTokenSource();
await DelayActionAsync(this.delayCancellationTokenSource.Token);
base.OnMouseLeftButtonDown(e);
}
catch (OperationCanceledException)
{
return;
}
finally
{
this.delayCancellationTokenSource?.Dispose();
this.delayCancellationTokenSource = null;
}
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
this.delayCancellationTokenSource?.Cancel();
StopProgressAnimation();
if (this.ClickMode is ClickMode.Release)
{
if (!this.isClickValid)
{
return;
}
}
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 DelayActionAsync(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();
StopProgressAnimation();
base.OnMouseLeave(e);
}
protected override async void OnKeyDown(KeyEventArgs e)
{
if (e.Key is Key.Enter or Key.Space)
{
if (this.isExecutingKeyAction)
{
return;
}
this.isExecutingKeyAction = true;
try
{
this.delayCancellationTokenSource = new CancellationTokenSource();
await DelayActionAsync(this.delayCancellationTokenSource.Token);
}
catch (OperationCanceledException)
{
return;
}
finally
{
this.delayCancellationTokenSource?.Dispose();
this.delayCancellationTokenSource = null;
}
}
base.OnKeyDown(e);
}
protected override void OnKeyUp(KeyEventArgs e)
{
if (e.Key is Key.Enter or Key.Space)
{
this.delayCancellationTokenSource?.Cancel();
StopProgressAnimation();
if (this.ClickMode is ClickMode.Release)
{
if (!this.isClickValid)
{
return;
}
}
this.isClickValid = false;
this.isExecutingKeyAction = false;
}
base.OnKeyUp(e);
}
private async Task DelayActionAsync(CancellationToken cancellationToken)
{
this.delayCancellationTokenSource = new CancellationTokenSource();
var delayDuration = TimeSpan.FromMilliseconds(this.DelayInMilliseconds);
StartProgressAnimation();
await Task.Delay(delayDuration, cancellationToken);
this.isClickValid = true;
}
private void StartProgressAnimation()
=> this.part_ProgressBar?.BeginStoryboard(this.progressStoryBoard, HandoffBehavior.SnapshotAndReplace, true);
private void StopProgressAnimation()
{
this.progressStoryBoard.Stop();
this.progressStoryBoard.Remove(this.part_ProgressBar);
}
}
Generic.xaml
问题回答
暂无回答
相关问题
WPF convert 2d mouse click into 3d space
I have several geometry meshes in my Viewport3D, these have bounds of (w:1800, h:500, d:25).
When a user clicks in the middle of the mesh, I want the Point3D of (900, 500, 25)...
How can I achieve ...
Editing a xaml icons or images
Is it possible to edit a xaml icons or images in the expression design or using other tools?
Is it possible to import a xaml images (that e.g you have exported) in the expression designer for editing?...
WPF: writing smoke tests using ViewModels
I am considering to write smoke tests for our WPF application. The question that I am faced is: should we use UI automation( or some other technology that creates a UI script), or is it good enough to ...
WPF - MVVM - NHibernate Validation
Im facing a bit of an issue when trying to validate a decimal property on domain object which is bound to a textbox on the view through the viewmodel.
I am using NHibernate to decorate my property on ...
How do WPF Markup Extensions raise compile errors?
Certain markup extensions raise compile errors. For example StaticExtension (x:Static) raises a compile error if the referenced class cannot be found. Anyone know the mechanism for this? Is it baked ...
WPF/Silverlight: data binding a datacontext to a property programmatically
I ve got some code which sets up a datacontext. Often enough, the datacontext should be set to some underlying data collection, such as an ObservableCollection - but occasionally I d like to set it ...
WPF design-time context menu
I am trying to create a custom wpf control, I m wondering how I can add some design-time features. I ve googled and can t seem to get to my goal.
So here s my simple question, how can I add an entry ...
How to combine DataTrigger and EventTrigger?
NOTE I have asked the related question (with an accepted answer): How to combine DataTrigger and Trigger?
I think I need to combine an EventTrigger and a DataTrigger to achieve what I m after:
when ...