【问题标题】:How to track number of spam clicks on a button in WPF如何跟踪WPF中按钮的垃圾邮件点击次数
【发布时间】:2016-07-21 22:53:49
【问题描述】:

我有一个按钮可以跳过 x 秒的视频。如果用户垃圾邮件单击该按钮,我的视频会一遍又一遍地更新,这是一项昂贵的操作。阻止用户向按钮发送垃圾邮件的最佳方法是什么?我正在使用路由的 ui 命令,并且想要将秒数相加并执行 1 次操作。延迟计时器是这里的最佳实践吗?延迟操作 10ms 并在每次点击时重置延迟?还是 wpf 中内置的东西可以提供帮助?

更新: 我想跟踪用户在垃圾邮件点击按钮期间的点击次数

【问题讨论】:

  • 当用户每秒点击 5 次时 - 您是希望每秒更新一次还是每 5 秒更新 5 次?
  • 我只希望更新发生一次,并通过按下按钮的次数来计算时间

标签: wpf button


【解决方案1】:

我真的希望异步方式有效,当我们尝试时我创建了一个解决方案,请随时告诉我我做错了什么以及任何不良做法。

我决定为此使用调度程序计时器,尽管我并不想这样做。在网上找不到任何更好的做法。

private TimeSpan overallSkipSpeed = TimeSpan.Zero;
private readonly TimeSpan Interval = TimeSpan.FromMilliseconds(400);
private DispatcherTimer _dispatcherTimer;
private TimeSpan _time;

// Execute command function
public void ExecuteClickCommand()
{
    // If the timer isn't going create and start it
    if (_dispatcherTimer == null)
    {
        overallSkipSpeed = TimeSpanToModifyBy(skipSpeed, skipForward);
        _time = Interval;

        _dispatcherTimer = new DispatcherTimer(Interval, DispatcherPriority.Normal, Tick, Application.Current.Dispatcher);
        _dispatcherTimer.Start();
    }
    else // If the timer is going reset to interval
    {
        // THIS IS WHERE I ADDED MY SKIP VALUES TOGETHER
        // So value from last click + value from this click

        _dispatcherTimer.Stop();
        _time = Interval;
        _dispatcherTimer.Start();
    }
}

// Method to run when timer ticks over
private void Tick(object sender, EventArgs eventArgs)
{
    // if the timer has reached below zero
    if (_time <= TimeSpan.Zero)
    {
        _dispatcherTimer.Stop();
        _dispatcherTimer = null;
        _time = TimeSpan.FromSeconds(0);

        // HERE IS WHERE WE CAN NOW SKIP VIDEO BY 
        // THE SKIP SPEED WE HAVE ACCUMULATED   
    }
    else
    {
        _time = _time.Add(-Interval);
    }
}

【讨论】:

    【解决方案2】:

    我更进一步,创建了我自己的命令。

    此命令的作用类似于中继命令,但如果您在初始化中设置延迟时间跨度,则会延迟。你可以在你的执行方法中检索它被点击的次数。

    初始化命令:

    ICommand DelayedClickCommand = new DelayedCommand(ExecuteDelayedClickCommand, TimeSpan.FromMilliseconds(200));
    

    创建一个执行方法并检索点击次数:

        private void ExecuteClickCommand()
        {
            TimesClicked = ((DelayedCommand)ClickCommand).TimesClicked;
        }
    

    这里是命令类:

    public class DelayedCommand : ICommand
    {
        private readonly Action _methodToExecute;
        private readonly Func<bool> _canExecuteEvaluator;
        private readonly DispatcherTimer _dispatcherTimer;
    
        public int TimesClicked;
    
        public event EventHandler CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }
    
    
        /// <summary>
        /// A command to stop the spamming of the <see cref="Execute"/> method
        /// </summary>
        /// <param name="methodToExecute">Method to run when command executes</param>
        /// <param name="canExecuteEvaluator">Method used to determine if the command can execute</param>
        /// <param name="delayTime">The cool down period required between click execution</param>
        public DelayedCommand(Action methodToExecute, Func<bool> canExecuteEvaluator, TimeSpan delayTime)
        {
            _methodToExecute = methodToExecute;
            _canExecuteEvaluator = canExecuteEvaluator;
    
            _dispatcherTimer = new DispatcherTimer(delayTime, DispatcherPriority.Normal, Callback, Application.Current.Dispatcher);
        }
    
        /// <summary>
        /// A command to stop the spamming of the <see cref="Execute"/> method
        /// when no <see cref="CanExecute"/> method is required
        /// </summary>
        /// <param name="methodToExecute">Method to run when command executes</param>
        /// <param name="delayTime">The cool down period required between click execution</param>
        public DelayedCommand(Action methodToExecute, TimeSpan delayTime)
            : this(methodToExecute, null, delayTime)
        {
        }
    
        /// <summary>
        /// A command when only a <see cref="Execute"/> method is needed
        /// </summary>
        /// <param name="methodToExecute">Method to run when command executes</param>
        public DelayedCommand(Action methodToExecute)
            : this(methodToExecute, null, TimeSpan.Zero)
        {
        }
    
        /// <summary>
        /// A command taking a <see cref="Execute"/> Method and a <see cref="CanExecute"/> method
        /// </summary>
        /// <param name="methodToExecute">Method to run when command executes</param>
        /// <param name="canExecuteEvaluator">Method used to determine if the command can execute</param>
        public DelayedCommand(Action methodToExecute, Func<bool> canExecuteEvaluator)
            : this(methodToExecute, canExecuteEvaluator, TimeSpan.Zero)
        {
        }
    
        public bool CanExecute(object parameter)
        {
            if (_canExecuteEvaluator == null)
            {
                return true;
            }
    
            return _canExecuteEvaluator.Invoke();
        }
    
        public void Execute(object parameter)
        {
            if (!_dispatcherTimer.IsEnabled)
                TimesClicked = 0;
    
            TimesClicked++;
    
            _dispatcherTimer?.Stop();
            _dispatcherTimer?.Start();
        }
    
        private void Callback(object sender, EventArgs eventArgs)
        {
            _dispatcherTimer.Stop();
            _methodToExecute.Invoke();
        }
    }
    

    注意:当您点击垃圾邮件时,执行此命令将在最后一次点击后 200 毫秒后运行,从而产生滞后效果。我已经向 git hub 添加了一个示例项目,并将在那里为这个问题添加更好的命令

    https://github.com/sgreaves1/DelayedCommands

    【讨论】:

      【解决方案3】:

      猜我是这里最懒的人...

      public class PostponeCommand : ICommand
          {
              private TimeSpan _delay;
              private Action<object> _command;
              private CancellationTokenSource _cancellation;
      
              public PostponeCommand(Action<object> command, int delayMs)
              {
                  this._command = command;
                  this._delay = TimeSpan.FromMilliseconds(delayMs);
              }
      
              public bool CanExecute(object parameter)
              {
                  return true;
              }
      
              public async void Execute(object parameter)
              {
                  _cancellation?.Cancel();
                  _cancellation = new CancellationTokenSource();
                  try
                  {
                      await Task.Delay(_delay, _cancellation.Token);
                      _command?.Invoke(parameter);
                  }
                  catch (TaskCanceledException ex)
                  {
                      // canceled
                  }
              }
      
              public event EventHandler CanExecuteChanged;
          }
      

      【讨论】:

      • 这只是基本上延迟了命令的执行。我正在寻找一种方法来跟踪该延迟期间的点击次数
      • @Sam 你的问题和 cmets 都没有表明......你应该编辑你的问题
      【解决方案4】:

      不确定内置 Command 是否能够执行此操作,但您可以延迟执行此操作(基于 cmets 更新):

      private int spamCount = 0;
      
      private int delayValue = 0;
      
      private object isHoldedLock = new object(); 
      
      private bool isHolded = false;
      
      public bool CanProceed(int delay, Action updateVideo)
      {
          lock (this.isHoldedLock)
          {
              if (this.isHolded)
              {
                  this.spamCount++;
      
                  this.delayValue = delay;
      
                  return false;
              }
      
              this.isHolded = true;
              this.delayValue = delay;
      
              Task.Run(async () =>
              {
                  while (this.delayValue > 0)
                  {
                      await Task.Delay(100);
      
                      this.delayValue -= 100;
                  }
      
                  updateVideo();
      
                  lock (this.isHoldedLock)
                  {
                      this.isHolded = false;
                  }
              });
      
              return true;
          }
      }
      

      以您需要的任何方式处理/重置spamCount 内的SkipVideo 值。

      并在您的命令处理程序中使用:

      private void InvokedFromCommand()
      {
          if (CanProceed(1000, SkipVideo()))
          {
              // SkipVideo();
          }
      }
      

      【讨论】:

      • 使用此代码可以在每次调用命令时重置延迟吗?
      • 在这种情况下,延迟将在用户点击按钮的 最新 时间后过期 - 如果用户将点击 10 秒而不停止,则只有在第十一次点击后才能跳过第二。是这个意思吗?
      • 不抱歉,我的意思是如果他点击一次,计时器就会启动,如果他在 10 秒内再次点击,计时器就会重新启动,如果他在那 10 秒后没有点击,我将更新视频。
      • 我更新了答案。如果需要,为delayValue 添加锁。
      • 延迟到期后忘记触发视频更新,再次更新答案。
      猜你喜欢
      • 2013-06-29
      • 2015-10-28
      • 1970-01-01
      • 1970-01-01
      • 2012-12-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多