Reputation: 1482
I have a user control with several child controls. I need the user interface to react to keypresses, so I decided to put the handling code in a MainControl_KeyDown event. However, when I press a key in my application, this event does not fire.
I have found a solution through a search engine which relies upon use of the Windows API, which I would like to avoid, as it seems like overkill for what should be a function that is properly supported by the .NET framework.
Upvotes: 13
Views: 31972
Reputation: 848
Since you are in a UserControl, you can simply override OnPreviewKeyDown
method as shown below:
protected override void OnPreviewKeyDown(KeyEventArgs e)
{
base.OnPreviewKeyDown(e);
if(e.Key == Key.Escape || e.SystemKey == Key.F10)
{
...
}
}
Upvotes: 0
Reputation: 1855
I have a trick.
UcBase
inherit from UserControl
UcSub1
and UcSub2
inherit from UcBase
.
UcSuperClass
inherit from UcBase
too.
UcSub1
, UcSub2
use within UcSuperClass
.
I mad UcSub1
and UcSub2
invoke ProcessCmdKey
.
Code:
public class UcBase : UserControl
{
public delegate bool ProcessCmdKeyHandler(Keys keyData);
public ProcessCmdKeyHandler KeyHandler;
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
KeyHandler += ProcessKey;
if (Parent != null)
{
var parent = GetParentControl<UcBase>(Parent);
if (parent != null)
{
parent.KeyHandler += ProcessKey;
}
}
}
protected virtual bool ProcessKey(Keys keyData)
{
return false;
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
const int WM_KEYDOWN = 0x100;
const int WM_SYSKEYDOWN = 0x104;
if (KeyHandler != null
&& (msg.Msg == WM_KEYDOWN) || (msg.Msg == WM_SYSKEYDOWN))
{
if (KeyHandler(keyData) == true)
{
return true;
}
}
return base.ProcessCmdKey(ref msg, keyData);
}
private T GetParentControl<T>(Control control)
where T : Control
{
T parentControl = default(T);
var queue = new Queue<Control>();
var targetControlType = typeof(T);
queue.Enqueue(control.Parent);
while (queue.Count > 0)
{
var parent = queue.Dequeue();
if (parent != null)
{
if (parent.GetType().BaseType == targetControlType)
{
parentControl = (T)parent;
break;
}
else
{
queue.Enqueue(parent.Parent);
}
}
else
{
break;
}
}
return parentControl;
}
}
public class UcSub1 : UcBase
{
protected override bool ProcessKey(Keys keyData)
{
// if you process do something, and return true then UcBase.ProcessCmdKey pass by.
return false;
}
}
public class UcSub2 : UcBase
{
protected override bool ProcessKey(Keys keyData)
{
// if you process do something, and return true then UcBase.ProcessCmdKey pass by.
return false;
}
}
public class UcSuperClass : UcBase
{
private UcSub1 _ucSub1;
private UcSub2 _ucSub2;
public UcSuperClass()
{
_ucSub1 = new UcSub1();
_ucSub2 = new UcSub2();
}
protected override bool ProcessKey(Keys keyData)
{
// if you process do something, and return true then UcBase.ProcessCmdKey pass by.
return false;
}
}
Upvotes: 0
Reputation: 8171
You can add following method to your usercontrol
:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if ((keyData == Keys.Right) || (keyData == Keys.Left) ||
(keyData == Keys.Up) || (keyData == Keys.Down))
{
//Do custom stuff
//true if key was processed by control, false otherwise
return true;
}
else
{
return base.ProcessCmdKey(ref msg, keyData);
}
}
Upvotes: 23
Reputation: 2807
I know this thread is a bit old, but I had a similar problem and handled it in a different way:
In the main-window I changed the KeyPreview attribute to true.
I registered the KeyDown-event handler of the main window in my user control.
this.Parent.KeyDown += new KeyEventHandler(MyControl_KeyDown);
This prevents me from routing the KeyDown event of every child control to my user control.
Of course it's important to remove the event handler when you unload your user control.
I hope this helps people who face a similar problem now.
Upvotes: 10
Reputation: 12341
Here is an example that loop throw each control in the form to attach the KeyDown event. It's like the previouly answer in this post but handle more cases:
using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
public class UserControlKeyboardProcessor
{
private void Control_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
base.OnKeyDown(e);
}
private void UserControlKeyboardProcessor_Disposed(object sender, System.EventArgs e)
{
foreach (System.Windows.Forms.Control control in this.GetAllControls(this)) {
control.KeyDown -= Control_KeyDown;
}
}
private void UserControlKeyboardProcessor_Load(object sender, System.EventArgs e)
{
foreach (System.Windows.Forms.Control control in this.GetAllControls(this)) {
control.KeyDown += Control_KeyDown;
}
}
public Generic.List<System.Windows.Forms.Control> GetAllControls(System.Windows.Forms.Control control)
{
Generic.List<System.Windows.Forms.Control> controls = new Generic.List<System.Windows.Forms.Control>();
foreach (System.Windows.Forms.Control subControl in control.Controls) {
controls.Add(subControl);
controls.AddRange(this.GetAllControls(subControl));
}
return controls;
}
public UserControlKeyboardProcessor()
{
Load += UserControlKeyboardProcessor_Load;
Disposed += UserControlKeyboardProcessor_Disposed;
}
}
Upvotes: 1
Reputation: 65411
You could add a KeyDown event handler for every child control in your user control and fire the KeyDown event of your user control in each, like so:
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
this.OnKeyDown(e);
}
Upvotes: 8
Reputation: 126
maybe you should handle all the events locally and then fire dummy events to communicate with the main control?
or maybe this may be a focus issue; if there are many child controls and only one of them is focused, the other ones would not react the key down action.
maybe you could post some code snippets here to be sure.
Upvotes: 1