Click here to Skip to main content
15,868,016 members
Articles / Programming Languages / Visual Basic
Article

Overriding Keydown in a User Control using ProcessKeyPreview

Rate me:
Please Sign up or sign in to vote.
4.68/5 (14 votes)
7 Feb 2008CPOL2 min read 102.9K   38   19
How to easily override key processing in a form or user control

Introduction

Recently I was working on a very complex user control with lots of child controls on it and wanted to be able to override the handling of the keydown event in a single place (the main user control).

I added the keydown event to my user control and noticed that it never got fired. This seemed to be because the child controls were handling them instead and not passing them to the main control.

On a form, you can set Form.KeyPreview to True which will allow the form to receive key events before they are passed to the control that has focus. Unfortunately, this is not available on user controls.

Some searching on the Internet revealed that the ProcessKeyPreview event which when overridden in a user control will allow you to trap the keyboard messages before the child controls get them.

Unfortunately the ProcessKeyPreview is not very friendly and passes you the Windows messages. This means you need to know the message number, handle repeating keys, handle control keys, etc.

I did a bit of reflecting on the framework and found that it's actually pretty easy to turn the messages into standard keydown and keyup events which makes it much easier to code.

I thought someone may find it useful.

Using the Code

To use the code, simply paste it into your control. Then you just need to decide whether you need Keydown and/or keyup events and implement them as you see fit.

C#
//----------------------------------------------
// Define the PeekMessage API call
//----------------------------------------------

private struct MSG
{
    public IntPtr hwnd;
    public int message;
    public IntPtr wParam;
    public IntPtr lParam;
    public int time;
    public int pt_x;
    public int pt_y;
}

[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern bool PeekMessage([In, Out] ref MSG msg, 
    HandleRef hwnd, int msgMin, int msgMax, int remove);

//----------------------------------------------
 
/// <summary> 
/// Trap any keypress before child controls get hold of them
/// </summary>
/// <param name="m">Windows message</param>
/// <returns>True if the keypress is handled</returns>
protected override bool ProcessKeyPreview(ref Message m)
{
    const int WM_KEYDOWN = 0x100;
    const int WM_KEYUP = 0x101;
    const int WM_CHAR = 0x102;
    const int WM_SYSCHAR = 0x106;
    const int WM_SYSKEYDOWN = 0x104;
    const int WM_SYSKEYUP = 0x105;
    const int WM_IME_CHAR = 0x286;

    KeyEventArgs e = null;

    if ((m.Msg != WM_CHAR) && (m.Msg != WM_SYSCHAR) && (m.Msg != WM_IME_CHAR))
    {
        e = new KeyEventArgs(((Keys)((int)((long)m.WParam))) | ModifierKeys);
        if ((m.Msg == WM_KEYDOWN) || (m.Msg == WM_SYSKEYDOWN))
        {
            TrappedKeyDown(e);
        }
        //else
        //{
        //    TrappedKeyUp(e);
        //}
            
        // Remove any WM_CHAR type messages if supresskeypress is true.
        if (e.SuppressKeyPress)
        {
            this.RemovePendingMessages(WM_CHAR, WM_CHAR);
            this.RemovePendingMessages(WM_SYSCHAR, WM_SYSCHAR);
            this.RemovePendingMessages(WM_IME_CHAR, WM_IME_CHAR);
        }

        if (e.Handled)
        {
            return e.Handled;
        }
    }
    return base.ProcessKeyPreview(ref m);
}

private void RemovePendingMessages(int msgMin, int msgMax)
{
    if (!this.IsDisposed)
    {
        MSG msg = new MSG();
        IntPtr handle = this.Handle;
        while (PeekMessage(ref msg, 
        new HandleRef(this, handle), msgMin, msgMax, 1))
        {
        }
    }
}

/// <summary>
/// This routine gets called if a keydown has been trapped 
/// before a child control can get it.
/// </summary>
/// <param name="e"></param>
private void TrappedKeyDown(KeyEventArgs e)
{
    if (e.KeyCode == Keys.A)
    {
        e.Handled = true;
        e.SuppressKeyPress = true;
    }
}

Points of Interest

Note that the ProcessKeyPreview can return true or false to indicate whether the keypress has been handled. However, if you are not handling it you should defer to the base ProcessKeyPreview method.

Note: ModifierKeys is part of the base Control class and returns a value indicating which of the modifier keys (SHIFT, CTRL, and ALT) is in a pressed state. You can set this in the KeyEventArgs as per usual...

History

  • 02-Jan-2008
    • Added Processing to remove messages if e.SuppressKeyPress is set in the KeyDown event
    • Changed message numbers to constants to make it easier to read
  • 07-Feb-2008
    • Removed references to Form to avoid confusion and mentioned KeyPreview

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionExcellent code! Pin
Johnny J.21-Mar-19 0:44
professionalJohnny J.21-Mar-19 0:44 
GeneralGreat!! Pin
Gilbert Consellado20-Jun-15 22:33
professionalGilbert Consellado20-Jun-15 22:33 
QuestionThank you! Pin
Dave13Cox27-Apr-15 11:16
Dave13Cox27-Apr-15 11:16 
QuestionArrow-Keys + KeyUp Pin
abfl2-Nov-14 12:43
abfl2-Nov-14 12:43 
GeneralMy vote of 5 Pin
cws2_na24-Jun-13 3:16
cws2_na24-Jun-13 3:16 
GeneralKeyUp doesn't fire !!! Pin
S.Reda1-Mar-11 23:45
S.Reda1-Mar-11 23:45 
GeneralThe way of the Form - very short :) Pin
Michael Ochmann28-Oct-10 3:02
Michael Ochmann28-Oct-10 3:02 
GeneralRe: The way of the Form - very short :) Pin
Aussie ALF8-Apr-11 1:27
Aussie ALF8-Apr-11 1:27 
Generalit does help, Thanks! Pin
liwenjiede2-Aug-10 17:24
liwenjiede2-Aug-10 17:24 
GeneralThe easiest way Pin
ThomasNNTVOG6-Oct-08 7:51
ThomasNNTVOG6-Oct-08 7:51 
GeneralThe Ultimate Easiest Way. Pin
ThomasNNTVOG6-Oct-08 8:09
ThomasNNTVOG6-Oct-08 8:09 
GeneralThanks Pin
Ilíon8-Jan-08 4:52
Ilíon8-Jan-08 4:52 
QuestionWhat about Form.KeyPreview = true? Pin
leppie2-Jan-08 13:50
leppie2-Jan-08 13:50 
AnswerRe: What about Form.KeyPreview = true? Pin
Laughing.John3-Jan-08 2:47
Laughing.John3-Jan-08 2:47 
GeneralRe: What about Form.KeyPreview = true? Pin
leppie3-Jan-08 4:01
leppie3-Jan-08 4:01 
GeneralRe: What about Form.KeyPreview = true? Pin
Laughing.John3-Jan-08 4:51
Laughing.John3-Jan-08 4:51 
Generalless complicated Pin
Mr.PoorEnglish1-Jan-08 13:57
Mr.PoorEnglish1-Jan-08 13:57 
GeneralRe: less complicated [modified] Pin
Laughing.John2-Jan-08 3:41
Laughing.John2-Jan-08 3:41 
GeneralFine :o) Pin
Mr.PoorEnglish2-Jan-08 23:42
Mr.PoorEnglish2-Jan-08 23:42 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.