Search code examples
c#winformskeypresskeydown

Stopping keys from repeating (C#)


In this application, I need to be able to stop the response from a key which is held down in order to prevent unnessecary data from entering the output. The problem I'm having is, using the methods in my code below does prevent the keys from repeating, but it also stops them from being responsive enough - as the users are hitting the keys very quickly.

I'm not sure if it's my hardware, api restriction or a problem with my code, but the routines I have below do not simply come round fast enough to work without making the program impossible to use. A way of identifying if a key is being actively held down (and for how long) would also help another feature for the program and solve this current issue.

Any ideas?

private void Form1_KeyDown(object sender, KeyEventArgs e)
{
    e.SuppressKeyPress = isKeyDown;
    isKeyDown = true;
}

private void Form1_KeyUp(object sender, KeyEventArgs e)
{
    isKeyDown = false;
}

private void Form1_KeyPress(object sender, KeyPressEventArgs e)
{
    if (!isStreamPlaying) return;
    if (e.KeyChar.Equals('d') || e.KeyChar.Equals('j'))
    {
        //red hit
        SoundPlayer hitSounds = new SoundPlayer(taikoLiveMapper.Properties.Resources.normal_hitnormal);
        hitSounds.Play();
        outputlist.Add(string.Format("320,240,{0},1,{1}", ms, 0));
        lastms = ms;
    }
    else if (e.KeyChar.Equals('s') || e.KeyChar.Equals('k'))
    {
        //blue hit
        SoundPlayer hitSounds = new SoundPlayer(taikoLiveMapper.Properties.Resources.normal_hitclap);
        hitSounds.Play();
        outputlist.Add(string.Format("320,240,{0},1,{1}", ms, 8));
        lastms = ms;
    }
}

Solution

  • You can use GetKeyState to find out if a key is down and use that to track the keys:

        [DllImport("user32.dll")]
        static extern short GetKeyState(int key);
    
        static bool IsKeyPressed(Keys key)
        {
            short state = GetKeyState((int)key);
            return ((state & 128) != 0);
        }
    
        int i = 0;
    
        Dictionary<Keys, DateTime> downSince = new Dictionary<Keys, DateTime>();
    
        private void UpdateKeyStates()
        {
            foreach (var entry in downSince.ToArray())
            {
                if (!IsKeyPressed(entry.Key))
                    downSince.Remove(entry.Key);
            }
        }
    
        private void Form1_KeyDown(object sender, KeyEventArgs e)
        {
            UpdateKeyStates();
            if (!downSince.ContainsKey(e.KeyCode))
            {
                downSince.Add(e.KeyCode, DateTime.UtcNow);
                i++;
    
            }
            Text = i.ToString() + " " +(int)(DateTime.UtcNow - downSince[e.KeyCode]).TotalMilliseconds;
        }
    
        private void Form1_KeyUp(object sender, KeyEventArgs e)
        {
            UpdateKeyStates();
        }
    

    This example counts i up every time a key is pressed, and shows for how long it has been pressed. It uses GetKeyState instead of tracking KeyDown/KeyUp since you might miss those messages if something else has focus.