取消控件上的一些键盘事件

本文关键字:键盘 事件 控件 取消 | 更新日期: 2023-09-27 17:52:12

我有一个DataGridView与KeyDown和KeyUp处理程序。在某些情况下,我想禁用Enter键的默认行为(取消选择文本并关注下一行),就像这样:

    private void View_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.Enter && Condition)
        {
            // Special flow - do logic and CANCEL default event effect
            SpecialFlow = true;
            ...
            e.Handled = true; // That doesn't do the job
        }
    }
    private void View_KeyUp(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.Enter && Condition && SpecialFlow)
        {
            // Special flow - do logic and continue normally
            SpecialFlow = false;
            ...
        }
    }


我找到了一些似乎不符合我需要的解决方案:

  • 使用钩子拦截应用程序中的所有键盘事件-我需要更多的检查来做到这一点。
  • 删除所有事件处理程序-但我需要他们回来一旦键。
  • 实现一个新的控制-过量杀伤。

简单地说,是否有一种方法可以在处理程序之后(就在默认处理之前)并且仅在特殊流中拦截键事件?

解决:

我遇到的问题是KeyDown根本没有被调用,因为单元格处于编辑模式,基本上没有办法防止按Enter结束编辑模式的默认行为。所以我添加了一个标志,用于在编辑模式结束后返回到编辑模式下的选定文本-在KeyUp处理程序中。

就像这样:

    private void View_CellEndEdit(object sender, DataGridViewCellEventArgs e)
    {
        if (Condition)
            EndEditFlag = true;
    }
    private void View_KeyUp(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.Enter && Condition)
        {
            if (EndEditFlag)
            {
                EndEditFlag = false;
                // Select by previously saved selection data - revert CellEndEdit
                View.CurrentCell = View.Rows[...].Cells[...];
                SelectText(...);
            }
            // Special flow - do logic
        }
    }

取消控件上的一些键盘事件

我不确定是否正确理解了你的问题。但是如果我没有错的话,你所需要的只是KeyEventArgs对象上的"Handled"属性。

当你在事件处理程序中设置该属性为"true"时,该特定事件的进一步处理将被调用:

    public void Test()
    {
        DataGridView view = new DataGridView();
        view.KeyDown += view_KeyDown;
    }
    void view_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.KeyCode == Keys.Enter /* && some other conditions */)
        {
            //Do some custom logic
            e.Handled = true; //Cencel event. Avoid any other processing. Like the button was never pressed.
        }
    }

对此的深入解决方案可能会变得相当复杂。我建议先尝试一下KeyEventArgsSuppressKeyPress属性。如果在View_KeyDown中将其设置为true,则可能不会触发标准行为。如果是,就离开这里。

如果没有,您将不得不更改在击键时处理windows消息的方式。我就是这样做的,在我正在做的一个项目中。

我的方法的简短概述:将您的控件放入从UserControl下降的容器中。这允许用一些自定义代码重写ProcessKeyPreview方法,在这些自定义代码中完成了上述对windows消息的干扰。从本质上讲,来自user32.dll的PeekMessage用于在被覆盖的ProcessKeyPreview处理后将不需要的windows消息从消息队列中取出。

让我们来看看容器的代码:

public class baseKeyControl : UserControl
{
    private const int WM_KEYDOWN = 0x100;
    private const int WM_KEYUP = 0x101;
    const int WM_CHAR = 0x102;
    const int WM_SYSCHAR = 0x106;
    const int WM_SYSKEYDOWN = 0x104;
    const int WM_SYSKEYUP = 0x105;
    const int WM_IME_CHAR = 0x286;
    private struct MSG
    {
        public IntPtr hwnd;
        public int message;
        public IntPtr wParam;
        public IntPtr lParam;
        public int time;
        public int pt_x;
        public int pt_y;
    }
    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern bool PeekMessage([In, Out] ref MSG msg,
                                               HandleRef hwnd, int msgMin, int msgMax, int remove);        
    /// <summary>
    /// Trap any keypress before child controls get hold of them
    /// </summary>
    /// <param name="m">Windows message</param>
    /// <returns>True if the keypress is handled</returns>
    protected override bool ProcessKeyPreview(ref Message m)
    {
        KeyEventArgs e = null;
        if ((m.Msg != WM_CHAR) && (m.Msg != WM_SYSCHAR) && (m.Msg != WM_IME_CHAR))
        {
            e = new KeyEventArgs(((Keys)((int)((long)m.WParam))) | ModifierKeys);
            if ((m.Msg == WM_KEYDOWN) || (m.Msg == WM_SYSKEYDOWN))
            {
                this.DoTrappedKeyDown(e);
            }
            else if ((m.Msg == WM_KEYUP) || (m.Msg == WM_SYSKEYUP))
            {
                this.DoTrappedKeyUp(e);
            }
            // Remove any WM_CHAR type messages if supresskeypress is true.
            if (e.SuppressKeyPress)
            {
                this.RemovePendingMessages(WM_CHAR, WM_CHAR);
                this.RemovePendingMessages(WM_SYSCHAR, WM_SYSCHAR);
                this.RemovePendingMessages(WM_IME_CHAR, WM_IME_CHAR);
            }
            if (e.Handled)
            {
                return e.SuppressKeyPress;
            }
        }
        return base.ProcessKeyPreview(ref m);
    }
    private void RemovePendingMessages(int msgMin, int msgMax)
    {
        if (!this.IsDisposed)
        {
            MSG msg = new MSG();
            IntPtr handle = this.Handle;
            while (PeekMessage(ref msg,
                               new HandleRef(this, handle), msgMin, msgMax, 1))
            {
            }
        }
    }
    public void DoTrappedKeyDown(KeyEventArgs e)
    {
        // Do your key down work here
    }
    public void DoTrappedKeyUp(KeyEventArgs e)
    {
        // Do your key up work here
    }
}

请注意错误,因为我已经快速复制了&从我现有的代码中将这些粘贴在一起,这些代码可以根据我的特定用例定制更多的功能。出于可读性的考虑,我不想把它放在这里。