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:
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);
}
}
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:
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:
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;
}
}