Capturing KeyDown events in a UserControl

早过忘川 提交于 2019-12-22 05:14:11

问题


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.


回答1:


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



回答2:


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



回答3:


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.




回答4:


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.




回答5:


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



回答6:


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



回答7:


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)
    {
        ...
    }
}


来源:https://stackoverflow.com/questions/1152784/capturing-keydown-events-in-a-usercontrol

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!