Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Win Forms UserControl not detecting key presses

There are a lot of questions about this (one, two, three, four, five), but I tried the fixes in all of them and they either don't work or don't suit my purposes. Here is my basic structure:

User Control
|-Panel
  |-Picture Box (several of them, created at runtime, do not exist at design time)

Because I think it is relevant, the Panel has its dock set to "fill" and resize set to "grow and shrink", so it always covers the entire user control. The PictureBoxes always cover a portion of the panel, but usually not all of it (although it is possible).

I am specifically listening for Ctrl + C, and I need a method that can respond regardless of which child has focus. I'd like a method that can listen for arbitrary key presses so I can expand on it later.

One of the answers on the linked pages suggests making a global listener for those key presses, I don't want to do that since I don't want it going off if it is a background application. Another suggests detecting it in the top level form and filtering it down to my User Control. The problem is that the User Control on down is being built out as a DLL, and I don't want to force the application using it to have to implement listening for Ctrl + C, this is something that it should be handling on its own.

Why the links above didn't work for me

1) I have no KeyPreview property to set to true on my UserControl. The second answer on that question suggests overriding ProcessCmdKey, which I did, but the callback is never called no matter what I try.

2) This one also suggests overriding ProcessCmdKey. As I said, it is never called.

3) There is no accept button for me to set to true.

4) The KeyDown and PreviewKeyDown callbacks have both been implemented, neither is ever called.

5) Also suggests ProcessCmdKey.

How can I detect key events at the User Control level regardless of focus? Alternatively, if the above methods I tried should be working, what settings have I missed are preventing it from working?

like image 636
Cody Avatar asked Aug 16 '16 17:08

Cody


2 Answers

OP: I am specifically listening for Ctrl + C, and I need a method that can respond regardless of which child has focus.

If you want to handle a key combination like Ctrl+C from your control even if it doesn't have focus or it's not selectable, you can add an invisible MenuStrip to your user control and add an item to it and assign the shortcut to it. Then handle click event of item and do what you need.

The click event will be raised each time the user press Ctrl+C even if your control doesn't contain focus.

You can also do it using code:

public UserControl1()
{
    InitializeComponent();
    var menu = new MenuStrip();
    var item = new ToolStripMenuItem();
    item.ShortcutKeys = System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.C;
    item.Click += item_Click;
    menu.Items.Add(item);
    menu.Visible = false;
    this.Controls.Add(menu);
}
void item_Click(object sender, EventArgs e)
{
    MessageBox.Show("Ctrl + C");
}

Note

You can't handle key events without having focus, but using a MenuStrip you can trap shortcut keys which you want using above method.

The reason which makes it working is, the Form is ContainerControl and ContainerControl calls ToolStripManager.ProcessCmdKey method in ProcessCmdKey which cause processing shortcuts of all non-contextmenu strips of the ToolStripManager.
For more information take a look at source code for ContainerControl.ProcessCmdKey.

like image 144
Reza Aghaei Avatar answered Nov 11 '22 02:11

Reza Aghaei


Keystroke events are fired on the control that has the focus. You picked controls that do not like to get the focus, don't show the focus, and have no use for keystrokes themselves. Which does beg the question how the user of your app could possibly know what Ctrl+C is going to do.

I'll assume that Ctrl+C is supposed to copy the image in the PictureBox to the clipboard. So best thing to do is to derive your own class from PB and modify it so it can be selected and shows focus. Add a new class to your project and paste the code shown below. Compile. Drag it from the top of the toolbox, replacing the PB in your user control.

using System;
using System.Windows.Forms;
using System.Drawing;

class SelectablePictureBox : PictureBox {
    public SelectablePictureBox() {
        this.SetStyle(ControlStyles.Selectable, true);
        this.TabStop = true;
    }
    protected override void OnMouseDown(MouseEventArgs e) {
        if (e.Button == MouseButtons.Left) this.Focus();
        base.OnMouseDown(e);
    }
    protected override void OnEnter(EventArgs e) {
        this.Invalidate();
        base.OnEnter(e);
    }
    protected override void OnLeave(EventArgs e) {
        this.Invalidate();
        base.OnLeave(e);
    }
    protected override void OnPaint(PaintEventArgs e) {
        base.OnPaint(e);
        if (this.Focused) {
            var rc = this.DisplayRectangle;
            rc.Inflate(new Size(-2, -2));
            ControlPaint.DrawFocusRectangle(e.Graphics, rc);
        }
    }
}
like image 37
Hans Passant Avatar answered Nov 11 '22 02:11

Hans Passant