I have written (mostly) with some take from a simple tutorial a calculator control for a program I am working on, the control works fine, except i would like the control to accept key input, like the windows calculator does, i.e. asterisk activates multiplication, enter evaluates and the numbers are number ect...

the control inherits from UserControl, and just contains the .net button controls.

since it inherits from UserControl it has the events for keyDown KeyUp and KeyPress.

the user control is contained in a tabcontrol, and is on a form that is a fixed tool window and an mdi child.

I would like for the control to accept keys when that tab is open and the toolwindow has focus. but alone, the usercontrol, even when focused the events don't fire.

I ended up just subscribing to the forms key events and calling the handlers on the usercontrol from there and just passing the eventargs on to the usercontrol, this didn't quite work... I failed to be able to handle the events so that the focus didn't jump around with the arrow keys, and the enter key just activates the focused button. also the enter key never fires any events.

I am just not sure where to go to from here. any suggestions would be appreciated.

I didn't figure out exactly how to do what I had wanted, but I did achieve the end result for my program. I ended up setting all the click event methods to internal, and calling them from the keydown event of the form.

I can't seem to handle the arrow keys though, I would like to keep the arrow keys from changing the selected control in my user control. i would prefer them to just do nothing, i tried setting a flag for all not used keys to throw, then the keypress even to check for the flag and handle them, but the arrows still don't get suppressed. any ideas?

Maybe this helps :

// This event occurs after the KeyDown event and can be used to prevent
// characters from entering the control.
private void MyButton_KeyPress(object sender, KeyPressEventArgs e)
    if (e.KeyCode == anarrowkey)
        // Stop the character from being entered into the control.
        e.Handled = true;

that's what I tried essentially. it just had no effect.

does each control throw its own key events?

that is if I handle a key on the form, does that event still occur on a child control? because my form's keypress event handles the keys i would like to have no effect on its child controls, seems they all still weasel through.

You can only handle Keydowns etc. for a control who has the focus.
Seems logical : if you have 2 textboxes 1 will have the focus so it can accept key input.
When you open an empty form it will have the focus, but as soon as you put lets say a button on it, the button will have the focus. You can set the focus with the Focus() method. Hope this helps a bit.

ah. that makes sense, so a control must have focus, but what if a child control, on a control has focus?

I have a form, which contains a tabcontol, which contains a user control i have crated, the user control is a calculator that contains 2 groupboxes, on one, a textbox, and on the other a set of common calculator buttons,

if only the control with focus raises the events, there is my issues. because when my user control gains focus, it passes it on to the first button, and else afterward the last button to be focused on previously.

do I need to create a custom button control that inherits from the button class and passes its key events to its parents of type user control?

or is there a better way to go about it?

It is a mere coincidence that I happen to arrive in the same situation as you. My calculator has a form, a textbox and some flowlayout panels I use to lay out my custom(round) calculator buttons. Clicking with the mouse works fine, except I am now trying to add some keyboard functionality just as you are trying to do. Still working on it for the moment, we'll keep in touch!

thanks, I will post back if I find a better way to do it. but currently I'm using the form's keydown event to call internally declared event methods in the control that are also the click event handlers, and the last line of all the methods it tells the equal button to take focus, ensuring that the enter key will invoke the the equals method, But this is not a good way to go about it, and certainly not a simple drop in user control.

but it is an acceptable temporary solution, as the project I have started is large, and a convenient calculator is but on feature that I am working on.

I don't program for money, It's not my job, I'm not in school, I don't intend to rule the world, I simply am unhappy with the selection of software out there, and I intend to do something about it.

Until then happy coding!

I don't program for money, It's not my job, I'm not in school, I don't intend to rule the world

I see we have even other things in common!
May I point your attention to this snippet, I intend to use it in some way in my calculator :

I don't code on a daily basis and I'm working on different projects so this might take a while. I don't know, I have no deadlines:cool:

Let you know of my progress so I can show you code how I did it. Might give you some ideas.
Until then, happy coding!

thanks ddanbe,
I the method I am currently using is handling the forms keydown and keypress events. and I do have the forms keypreview set to true. thats how I have it working now, and I am using a very similar system as the code snippet you linked to, for the handling of unwanted keypress(s) but for some reason Its not keeping the focus from changing when arrows are pressed, and nor is it keeping the return keys from activating the focused buttons, instead of just the code i intend.

What I can't seem to accomplish is that preventing, and also I would like this control to be a drop in control, that works as long as the form that contains it has focus...

With that said, Is there a way to subscribe to a parent forms events from a child control?


this.Parent.keyDown += new System.EventHandler(this.CalckeyDown);

Would that work?
assuming that the parent was a form and not, like in my case, a tabcontol or panel or somthing?

When I get a chance I will create a test project and try it out, I'm just really pressed for time these days.

and living without a deadline is wonderful isn't it? If I had deadlines I would be dead from a stroke by now.

If you're not catching a key in the KeyDown event then it is probably a command key, and you need to catch it another way. Try this:

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;
       	return base.ProcessCmdKey(ref msg, keyData);

ok. so there is a process command key event ? I was unaware of this, would that be declared on the end control, or on the active form?

and as I can see its an override, so I don't have to instantiate the event handler correct?

well, im not sure if it works on the end control, but it does work well on the form. just as easy as pasting it in has the exact desired effect. Thank you very much sknake.

But I can't quite call this one solved yet. but I have learned a lot on the way through. I still have my original pressing issue. I can't seem to create a simple user control that handles everything by just being dropped in. although I think subscribing to the parent event might work, I'm still unsure...

and even if it is as simple as this.Parent.event += new Event Handler(this.a local control function); does work, could you make it somehow find the top parent to really be getting the forms events and not a tabcontrol, or panels events?

I don't think that you can just drop a user control on a form and have it take control of the keys like that. I cannot think of any instance of this being done by third party software or microsoft... but it may be possible. One thing you could do is descend from a : Form and write the event passing code, then use that as your form base. As to find the parent form for a control you can keep crawling up .Parent until you find a form:

private void simpleButton4_Click(object sender, EventArgs e)
			Control ctrl = (sender as Control);
			while (!(ctrl is Form))
				ctrl = ctrl.Parent;
			Form f = (ctrl as Form);

Here is my output:


Then you see a message box with the form's caption

Be cautious on crawling the parent of controls. The .NET framework uses delayed initialization and the control is not completely created until the _Load event is fired. So if your control is on the 2nd tab page and you run code against the control you may run in to problems. In places where I do this I iterate all the tab pages and force them to show, then show the first tab page again all in the form load event. This forces all of the form controls to be created when the form loads.

I just remember a while back that I wrote a form control that allowed for clean transparent edges by piling up 2 forms, one that performed a C# transparency workaround, and another that kept position center of it that held the controls. the control form created the background form and passed to its constructor the instance of the control form, then to make the background form act like the control form for example the visibility be the same, I simply used that passed instance variable to declare some event handlers of the control form in the background form,

ParentForm.VisibleChanged += new EventHandler(parentFormVisible);

 void parentFormVisible(Object sender, EventArgs e)
            m_lwin.Visible = ParentForm.Visible;

so I don't see why this wouldn't work, I'm going to try it out as soon as I get off work tonight.

and thanks for that code snippet. I never though I sifting through parents until you found on of the right type. makes sense though.

happy coding!

Edited 3 Years Ago by Reverend Jim: Fixed formatting

wow, it worked perfect first try.

I used a modified version of your code to find the parent form, and then set the parent form's key preview to true and created a local event handler for the parents forms keydown event and it works like a charm, I now have a ready made drop in anywhere calculator control that accepts key events instantly with no additional code.

Control ctrl = (sender as Control);
            while (!(ctrl is Form))
                ctrl = ctrl.Parent;
            f = (ctrl as Form);

            f.KeyPreview = true;

                f.KeyDown += new KeyEventHandler(this.KeyHandler);

I just put that in my controls load event, and created a higher up scope of Form f; although its apparent now I didn't need too.

But then created a function named KeyHandler and treated it as a local KeyDown event handler, and it works withough a hitch. Im loving it. only problem is if its in a tabpage after the project is built it crashes the designer in VS. and wont let you view that page of the tab... Im not sure why. But it compiles fine, and runs fine. No errors.

so it is completely possible, completely simple, and I am completely happy. I may even need to post this on codeproject. Others should know its this simple.

Cheers fellows, and thanks for all your assistance!

Edited 3 Years Ago by Dani: Formatting fixed

With user controls visual studio actually runs the user control's load event. So in this case the UC is trying to find a parent form, which cannot be found. Use a modification of my code that should solve the problem:

Control ctrl = (sender as Control);
while (!(ctrl is Form))
  if (ctrl.Parent != null)
    ctrl = ctrl.Parent;
f = (ctrl as Form);
if (f != null) //when in design time it won't find the form
  f.KeyPreview = true;
  f.KeyDown += new KeyEventHandler(this.KeyHandler);

I don't have a compiler in front of me so it may have a syntax error....

Great tidbit of knowledge!

Thanks! I had no idea that visual studio processed the load event in the designer. That makes a lot of sense. I have learned much from you.

and it looks like its error clean, but idk. I didn't paste it, I just realized what you were getting at and edit my code to match.

This question has already been answered. Start a new discussion instead.