You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF击键动力学:飞行时间的正确测量方法咨询

Fixing Zero-Value Key Press Timing Issues & Optimization Tips

Hey there! Let's dig into why you're seeing zero values for your key press flight time and how to fix it, plus some tweaks to make your user identification app more robust.

Why You're Getting Zero Values

There are a few likely culprits here:

  1. Uncontrolled repeated Stopwatch.Start() calls: If the user holds down a key, the system triggers repeated KeyDown events by default. While Stopwatch.Start() resumes counting if already running, this can lead to messy state handling.
  2. Millisecond precision limits: ElapsedMilliseconds rounds down to the nearest whole millisecond. If a key press is faster than 1ms (totally possible with quick taps), it'll return 0.
  3. Untracked key state: Without checking if a key is already pressed, you might accidentally call Stopwatch.Stop() on a stopwatch that never started in rare edge cases like ultra-rapid taps.

Fixed Code Implementation

Here's a revised version of your code that addresses these issues, plus better practices for reliability:

// Track if each key is currently held down to avoid duplicate start/stop
private bool _isKeyADown = false;
private bool _isKeyODown = false;
// Use long for sums to avoid integer overflow (millisecond totals can add up quickly)
private long _totalATime = 0;
private long _totalOTime = 0;
private int _aCounter = 0;
private int _oCounter = 0;
// Separate stopwatches for each key to handle simultaneous presses without interference
private readonly Stopwatch _aStopwatch = new Stopwatch();
private readonly Stopwatch _oStopwatch = new Stopwatch();

private void Label_KeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.A)
    {
        if (!_isKeyADown)
        {
            _aStopwatch.Restart(); // Restart resets and starts in one clean step
            _isKeyADown = true;
        }
        e.Handled = true;
    }
    else if (e.Key == Key.O)
    {
        if (!_isKeyODown)
        {
            _oStopwatch.Restart();
            _isKeyODown = true;
        }
        e.Handled = true;
    }
    else
    {
        e.Handled = true;
    }
}

private void Label_KeyUp(object sender, KeyEventArgs e)
{
    if (e.Key == Key.A && _isKeyADown)
    {
        _aStopwatch.Stop();
        // Use Elapsed.TotalMilliseconds for sub-millisecond precision (avoids zero for fast taps)
        double pressTimeMs = _aStopwatch.Elapsed.TotalMilliseconds;
        _totalATime += (long)Math.Round(pressTimeMs); // Or store as double to preserve full precision
        _aCounter++;
        _isKeyADown = false;
        _aStopwatch.Reset();
    }
    else if (e.Key == Key.O && _isKeyODown)
    {
        _oStopwatch.Stop();
        double pressTimeMs = _oStopwatch.Elapsed.TotalMilliseconds;
        _totalOTime += (long)Math.Round(pressTimeMs);
        _oCounter++;
        _isKeyODown = false;
        _oStopwatch.Reset();
    }
}

Key Improvements in This Code

  • Per-key stopwatches: Each key gets its own stopwatch, so pressing A and O at the same time won't mess up either's timing.
  • Key state tracking: The _isKeyADown/_isKeyODown flags prevent duplicate start calls from repeated KeyDown events triggered by held keys.
  • Higher precision: Elapsed.TotalMilliseconds gives you sub-millisecond values (e.g., 0.6ms) instead of rounding down to 0—critical for capturing fast, unique user tap patterns.
  • Safer data types: Using long for total times avoids integer overflow if you collect hundreds or thousands of samples.

Additional Optimization Tips for User Identification

  • Store individual samples, not just sums: Instead of only tracking total time and count, keep a List<double> of each key's press duration. This lets you analyze variance, averages, and outliers—all essential for accurate user recognition.
  • Disable key repeat: If you don't want held keys to count as multiple presses, handle PreviewKeyDown and set e.Handled = true if the key is already down.
  • Abstract repeated logic: Create a helper method to handle key start/stop logic, so you don't duplicate code for every key you add later. For example:
    private void HandleKeyStart(ref bool isKeyDown, Stopwatch stopwatch)
    {
        if (!isKeyDown)
        {
            stopwatch.Restart();
            isKeyDown = true;
        }
    }
    
  • Validate data: Before calculating averages, check that your counter isn't zero to avoid division errors.

内容的提问来源于stack exchange,提问作者user9687180

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:46:27