【问题标题】:Cancel some keyboard events on a control取消控件上的一些键盘事件
【发布时间】:2015-06-03 01:25:23
【问题描述】:

我有一个带有 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
        }
    }

【问题讨论】:

    标签: c# .net datagridview event-handling


    【解决方案1】:

    我不确定我是否正确理解了您的问题。但是,如果我没记错的话,您所需要的只是 KeyEventArgs 对象上的“已处理”属性。

    当您在事件处理程序中将该属性设置为“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.
            }
        }
    

    【讨论】:

    • Thnx,但我忘了说我试过了,由于某种原因它不起作用。
    • 它应该可以解决问题。我猜你在其他部分犯了一些错误。如果您为“Enter”按钮进行了自己的实现,并且将事件设置为要处理,它将不会被转发到任何其他控件。如果调用了 e.Handled=true,请确保使用调试器。
    • 你说得对,我的问题是从编辑的单元格中获取处理程序。
    【解决方案2】:

    对此的深入解决方案可能会变得相当复杂。我建议首先尝试使用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
        }
    }
    

    请注意错误,因为我已经从我现有的代码中快速复制并粘贴了这个代码,它可以根据我的特定用例量身定制更多功能。出于可读性的原因,不想在这里。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-08-28
      • 1970-01-01
      • 2011-01-05
      • 1970-01-01
      • 1970-01-01
      • 2020-12-13
      • 1970-01-01
      相关资源
      最近更新 更多