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:
- Uncontrolled repeated
Stopwatch.Start()calls: If the user holds down a key, the system triggers repeatedKeyDownevents by default. WhileStopwatch.Start()resumes counting if already running, this can lead to messy state handling. - Millisecond precision limits:
ElapsedMillisecondsrounds down to the nearest whole millisecond. If a key press is faster than 1ms (totally possible with quick taps), it'll return 0. - 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/_isKeyODownflags prevent duplicate start calls from repeatedKeyDownevents triggered by held keys. - Higher precision:
Elapsed.TotalMillisecondsgives 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
longfor 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
PreviewKeyDownand sete.Handled = trueif 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
相关产品推荐
相关产品推荐

