Захват событий KeyDown в UserControl - PullRequest
6 голосов
/ 20 июля 2009

У меня есть пользовательский элемент управления с несколькими дочерними элементами управления. Мне нужно, чтобы пользовательский интерфейс реагировал на нажатия клавиш, поэтому я решил поместить код обработки в событие MainControl_KeyDown. Однако, когда я нажимаю клавишу в моем приложении, это событие не срабатывает.

Я нашел решение через поисковую систему, которая опирается на использование Windows API, чего я хотел бы избежать, так как кажется, что это избыточное решение для функции, которая должным образом поддерживается платформой .NET.

Ответы [ 7 ]

9 голосов
/ 08 июля 2011

Я знаю, что эта ветка немного старая, но у меня была похожая проблема, и я решил ее по-другому:
В главном окне я изменил атрибут KeyPreview на true. Я зарегистрировал обработчик событий KeyDown главного окна в своем пользовательском элементе управления.

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

Это не позволяет мне перенаправлять событие KeyDown каждого дочернего элемента управления на мой пользовательский элемент управления.
Конечно, важно удалить обработчик событий при выгрузке пользовательского элемента управления.

Надеюсь, это поможет людям, которые сейчас сталкиваются с подобной проблемой.

8 голосов
/ 07 февраля 2016

Вы можете добавить следующий метод к вашему 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);
   }
}
8 голосов
/ 20 июля 2009

Вы можете добавить обработчик событий KeyDown для каждого дочернего элемента управления в вашем пользовательском элементе управления и запустить событие KeyDown вашего пользовательского элемента управления в каждом из них, например, так:

private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
    this.OnKeyDown(e);
}
1 голос
/ 23 июня 2010

Вот пример, в котором цикл генерирует каждый элемент управления в форме, чтобы прикрепить событие 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 голос
/ 20 июля 2009

может быть, вам следует обрабатывать все события локально, а затем запускать фиктивные события для связи с основным элементом управления?

или, возможно, это может быть основной проблемой; если имеется много дочерних элементов управления, и только один из них находится в фокусе, другие не будут реагировать на действие нажатия клавиши.

может быть, вы могли бы опубликовать здесь некоторые фрагменты кода, чтобы быть уверенными.

0 голосов
/ 31 мая 2019

Поскольку вы находитесь в UserControl, вы можете просто переопределить метод OnPreviewKeyDown, как показано ниже:

protected override void OnPreviewKeyDown(KeyEventArgs e)
{
    base.OnPreviewKeyDown(e);
    if(e.Key == Key.Escape || e.SystemKey == Key.F10)
    {
        ...
    }
}
0 голосов
/ 03 июля 2015

У меня есть трюк.

UcBase наследуется от UserControl

UcSub1 и UcSub2 наследуются от UcBase. UcSuperClass тоже наследуется от UcBase.

UcSub1, UcSub2 использование в UcSuperClass.

Я схожу с ума UcSub1 и UcSub2 призываю 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;
    }
}
...