取消控件上的一些键盘事件
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.
}
}
对此的深入解决方案可能会相当复杂。我建议首先尝试 KeyEventArgs
的 SuppressKeyPress
属性。如果在 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
}
}
请注意错误,因为我已从我现有的代码中快速复制并粘贴了此代码,该代码的功能更多,适合我的特定用例。出于可读性原因,这里不想要这个。
我有一个带有 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.
}
}
对此的深入解决方案可能会相当复杂。我建议首先尝试 KeyEventArgs
的 SuppressKeyPress
属性。如果在 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
}
}
请注意错误,因为我已从我现有的代码中快速复制并粘贴了此代码,该代码的功能更多,适合我的特定用例。出于可读性原因,这里不想要这个。