在用户控件中捕获按键事件(KeyDown events)

13

我有一个包含多个子控件的用户控件。我需要用户界面对按键有反应,所以我决定在 MainControl_KeyDown 事件中编写处理代码。然而,当我在应用程序中按下一个键时,这个事件没有触发。

我通过搜索引擎找到了一种解决方案,它依赖于使用 Windows API,但我希望避免使用它,因为这似乎对于.NET框架应该正常支持的功能来说有些过度。


2
我发现这个答案是最有帮助的 - 你需要重写ProcessCmdKey()。 - bsegraves
这个链接应该是答案。 - OfficeAddinDev
7个回答

21

您可以将以下方法添加到您的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);
   }
}

2
这应该是被接受的答案。当你处理嵌套控件等时,所有键事件都可能变得混乱。这对我来说总是可靠的。 - Jeremy Morren
实际上,您可以使用此行将键传递给“普通”KeyDown事件:yourUC_KeyDown(this, new KeyEventArgs(keyData)); - TaW

10

我知道这个帖子有点老了,但是我遇到了类似的问题,并用不同的方式处理它:
在主窗口中,我将KeyPreview属性更改为true。 我在我的用户控件中注册了主窗口的KeyDown事件处理程序。

this.Parent.KeyDown += new KeyEventHandler(MyControl_KeyDown);

这样可以防止我将每个子控件的KeyDown事件路由到我的用户控件。
当然,在卸载您的用户控件时删除事件处理程序非常重要。
希望这能帮助那些面临类似问题的人。

当一个控件在多个表单中使用且捕获“Enter”调用表单中的方法时,这非常有效。 - Wayne

8

您可以为用户控件中的每个子控件添加一个KeyDown事件处理程序,并在其中触发用户控件的KeyDown事件,如下所示:

private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
    this.OnKeyDown(e);
}

真的吗?没有更好的方法吗? - Kamil
正如其他人在其他地方评论的那样,您应该调查一下 https://dev59.com/IHM_5IYBdhLWcg3w9oQA#35250290 是否更适合您的目的。 - Patrick McDonald

1
这是一个示例,循环遍历表单中的每个控件以附加KeyDown事件。它类似于此帖子中先前的答案,但处理更多情况:
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;
    }
}

1
也许你应该在本地处理所有事件,然后触发虚拟事件与主控件通信?
或者这可能是一个焦点问题;如果有很多子控件,只有其中一个被聚焦,其他的就不会响应按键动作。
也许你可以在这里发布一些代码片段以确保。

0

由于您在 UserControl 中,因此可以简单地重写以下 OnPreviewKeyDown 方法:

protected override void OnPreviewKeyDown(KeyEventArgs e)
{
    base.OnPreviewKeyDown(e);
    if(e.Key == Key.Escape || e.SystemKey == Key.F10)
    {
        ...
    }
}

0

我有一个技巧。

UcBase 继承自 UserControl

UcSub1UcSub2 也继承自 UcBaseUcSuperClass 也继承自 UcBase

UcSub1UcSub2UcSuperClass 中使用。

我让 UcSub1UcSub2 调用了 ProcessCmdKey

代码:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接