Click here to Skip to main content
15,884,298 members
Please Sign up or sign in to vote.
2.50/5 (2 votes)
See more:
The UserControl is not like Form component contains keypreview attribute,so how can i use keydown or keypress event like form component,my confuse is that the focus must be in one of it's component ,because usercontrol doesn't has keypreview attribute.
so how can i solve this question,thanks for answer.

What I have tried:

a part of my tried code as below:
private void tabControlAutomaticManual_ParentChanged(object sender, EventArgs e)
{
if (TopLevelControl is Form)
{
(TopLevelControl as Form).KeyPreview = true;
TopLevelControl.KeyDown += new KeyEventHandler(TopLevelControl_KeyDown);
TopLevelControl.KeyUp += new KeyEventHandler(TopLevelControl_KeyUp);
}
}

void TopLevelControl_KeyUp(object sender, KeyEventArgs e)
{

}

void TopLevelControl_KeyDown(object sender, KeyEventArgs e)
{

}
Posted
Updated 21-May-16 2:55am
v2
Comments
Sergey Alexandrovich Kryukov 20-May-16 23:24pm    
KeyPreview is just not needed for controls. Why? And your question lacks the description of the problem. What exactly do you want to achieve? The problem looks simple, but to help you, we need to understand it.
—SA
BillWoodruff 21-May-16 8:20am    
"KeyPreview is just not needed for controls."

That is an absurd generalization.
Sergey Alexandrovich Kryukov 21-May-16 9:02am    
It is not needed in certain sense, in System.Windows.Forms, for controls, there is another mechanism, ProcessKeyPreview, which you failed to explain.

At the same time, if you mean that having the same mechanism as in form, with KeyPreview and the possibility to use, say, KeyDown, KeyUp even if the focus is taken by a child control, I would probably agree. It would be more convenient for the application developer. So, I agree that "KeyPreview is just not needed for controls", it this sense, was not an accurate statement.

—SA

Note: an approach to use using the Win API functions has been described here on CodeProject: [^]. I prefer not to use such API dependent methods unless there is no alternative.

First, a UserControl is, by design, a "light-weight" container for other Controls; part of what makes it "lighter" is that its "message pump" does not get Key Events ... even though it does expose the Key- related Events in the Property Browser at design-time (a mistake, imho).

The UserControl "expects" to have Controls placed on it ... why else use it if not to contain Controls ? One of its contained Controls will (most likely) have focus, and will get Key Events. The Control in the UserControl that gets Key Events by default will probably be the one with the lowest 'TabOrder value.

So, let's change the focus here to getting Key Events for all Controls contained in the UserControl. Example:
C#
// in the body of the UserControl
public Func<string, Keys, bool> KBNotification { set; get; }

private void UserControl1_Load(object sender, EventArgs e)
{
    foreach (Control control in ControlUtilities.GetAllControls(this))
    {
        control.KeyDown += ControlKeyDown;
    }
}

private void ControlKeyDown(object sender, KeyEventArgs e)
{
    if (KBNotification != null)
    {
        e.SuppressKeyPress = KBNotification((sender as Control).Name, e.KeyCode);
    }
}

// a separate Class
// Control Utility Functions
public static class ControlUtilities
{
    public static IEnumerable<Control> GetAllControls(Control aControl)
    {
        Stack<Control> stack = new Stack<Control>();
    
        stack.Push(aControl);
    
        while (stack.Any())
        {
            var nextControl = stack.Pop();
    
            foreach (Control childControl in nextControl.Controls)
            {
                stack.Push(childControl);
            }
    
            yield return nextControl;
        }
    }
}
What's happening here:

1. we create a Delegate (using the 'Func syntax) that will take two parameters, a Control name, and a Keys Enumeration value, and return a boolean.

2. we wire-up a KeyDown EventHandler for all Controls in the UserControl that tests if the Delegate is defined, and, if it is defined, invokes it with the required arguments.

3. if the Delegate is invoked, and returns 'true, the KeyDown Event will be prevented from reaching the Control that got the KeyDown; if the Delegate returns false, the KeyDown is passed to the Control, and handled in the usual way.

Example of use: in a Form that hosts an instance of the UserControl:
C#
private void Form1_Load(object sender, EventArgs e)
{
    userControl11.KBNotification = UCKbNotification;
}

private bool UCKbNotification(string s, Keys keys)
{
    if(s == "textBox1") return true;
}
Here, if the TextBox named 'textBox1 in the UserControl gets a KeyDown, 'false will be returned, and the KeyDown suppressed.

But, while we can use this to modify the behavior of every Control in some ContainerControl, consider this:

1. on a practical basis, in most cases, you will not need key event interception on every Control in your ContainerControl, but, only on certain Controls.

So, why not simplify this, and simply define ... in the ContainerControl ... the Controls you do want key intercept installed:
C#
public List<Control> ControlsToGetKeyDown;

public Func<string, Keys, bool> KBNotification { set; get; }

private void UserControl1_Load(object sender, EventArgs e)
{
    ControlsToGetKeyDown = new List<Control>
    {
       textBox1, panel1.Controls["richTextBox1"]
    };

    foreach (Control control in ControlsToGetKeyDown)
    {
        control.KeyDown += ControlKeyDown;
    }
}
 
Share this answer
 
v3
Instead of this property, there is a virtual method System.Windows.Forms.Controls.ProcessKeyPreview:
Control.ProcessKeyPreview Method (Message) (System.Windows.Forms)[^].

The action of handling events which calls this method does not depend on the KeyPreview flag of the parent form; the events are always dispatched to this function. You can override it in your control type (not only UserControl) and put your processing in it. It if returns true, it will stop further handling of the event in the hierarchy of child controls.

—SA
 
Share this answer
 
Comments
BillWoodruff 21-May-16 9:53am    
My vote of #3: while using ProcessKeyPreview is a valid alternative here ... in fact, it's one I have used in the past when no simpler technique gave me what I wanted ... it is an advanced technique that requires the implementor to understand Win API Constants and WParam and LParam event argument parameters, and it requires some low-level stuff (for newcomers to C#) to be able to identify if the UserControl, rather than one of its "contained" Controls got the KeyDown (or other Key Event).

Another reason I would not recommend it in case of a UserControl is that ... as I state in the comments at the end of my response ... it is very rare to ever need to get an "all-control" notification of Key Events from the UserControl itself, rather than from individual Controls in the UserControl.

However, I'll be very happy to raise my vote to #5 here if you'll show the OP some working code using that technique, and explain to them what the required API Constants are, and how WParam and LParam are used in this scenario ... and, show how to distinguish a Key Event on the UserControl rather than on one of its contained Controls.

Please note, that: as I have said to you before, in my mind a vote of #3 is not a negative vote.

collegialy, Bill
Sergey Alexandrovich Kryukov 21-May-16 10:09am    
I agree. The only reason I added it is that, 1) it answers the inquirer's question, 2) it complements your otherwise incomplete answer; which provides some help but does not clarify on KeyPreview.

I don't want to spend all that time to show how to deal with WParam and LParam. I hope the inquirer can figure all that. (Yes, I tried it in code before posting, in the most basic form.) I would say, this function is one of the weak parts of .NET FCL.

You comment convinces me that the way shown in your question is more practical, so I up-voted it (by 4, already), but in the assumption that you add the mention of ProcessKeyPreview or credit my answer.

—SA

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

  Print Answers RSS
Top Experts
Last 24hrsThis month


CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900