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

Cancel some keyboard events on a control

我有一个带有 KeyDown 和 KeyUp 处理程序的 DataGridView。在某些情况下,我想禁用 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 用于从消息队列中获取不需要的 windows 消息,在它们被覆盖的 ProcessKeyPreview.

处理后

那么让我们深入研究容器的代码:

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

请注意错误,因为我已从我现有的代码中快速复制并粘贴了此代码,该代码的功能更多,适合我的特定用例。出于可读性原因,这里不想要这个。