我有一个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.
}
}
对此的深入解决方案可能会变得相当复杂。我建议先尝试一下KeyEventArgs
的SuppressKeyPress
属性。如果在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
}
}
请注意错误,因为我已经快速复制了&从我现有的代码中将这些粘贴在一起,这些代码可以根据我的特定用例定制更多的功能。出于可读性的考虑,我不想把它放在这里。