ASh*_*ASh 8 .net c# datagridview winforms
我们DataGridView在表单中有数据.为了使我们增加了快速搜索TextBox到DataGridView.Controls并突出其包含从文本框文本的单元格.
但是有一个问题.DataGridView的消耗Left箭头←,Right箭头→,Home和End(有或无Shift)键,即使光标在文本框,并且用户不能更改光标处或选择从键盘文本.
TextBox生成PreviewKeyDown事件,没有更多的事情发生
简化代码:
public partial class TestForm : Form
{
public TestForm()
{
InitializeComponent();
Width = 400;
Height = 400;
var txt = new TextBox { Dock = DockStyle.Bottom, BackColor = Color.Khaki };
var dgv = new DataGridView
{
Dock = DockStyle.Fill,
ColumnCount = 3,
RowCount = 5
};
dgv.Controls.Add(txt);
Controls.Add(dgv);
dgv.PreviewKeyDown += DgvOnPreviewKeyDown;
dgv.KeyDown += DgvOnKeyDown;
txt.PreviewKeyDown += TxtOnPreviewKeyDown;
txt.KeyDown += TxtOnKeyDown;
}
private void DgvOnPreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
Debug.WriteLine(String.Format("Dgv Key Preview {0}", e.KeyCode));
e.IsInputKey = true;
}
private void DgvOnKeyDown(object sender, KeyEventArgs e)
{
Debug.WriteLine(String.Format("Dgv Key {0}", e.KeyCode));
}
private void TxtOnPreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
Debug.WriteLine(String.Format("Txt Key Preview {0}", e.KeyCode));
}
private void TxtOnKeyDown(object sender, KeyEventArgs e)
{
Debug.WriteLine(String.Format("Txt Key {0}", e.KeyCode));
}
}
Run Code Online (Sandbox Code Playgroud)
键入123TextBox,然后尝试Right箭头,Left箭头End或Home.DataGridView更改选定的单元格但TextBox插入符号不移动.
如果没有DataGridView内部的TextBox工作得很好(例如,使用相同的方法将其添加到TreeView中时完全没问题).TextBox与浏览器中的快速搜索面板类似,必须位于DGV之上.将TextBox添加到表单(或者更具体地说,添加到DGV父级)会创建自己的一组问题(跟踪位置,大小,可见性......),这是不可接受的.
可以做些什么来确保TextBox接收这些键并更改插入位置或选择文本?
如果 TextBox 不在 DataGridView 内部,则可以正常工作(例如,使用相同方法将其添加到 TreeView 中时完全没有问题)
显然问题出在DataGridView. 这是因为DataGridView重写了该Control.ProcessKeyPreview方法:
当子控件收到键盘消息时,子控件将调用此方法。子控件在为消息生成任何键盘事件之前调用此方法。如果此方法返回 true,则子控件认为消息已处理并且不会生成任何键盘事件。
该DataGridView实现就是这样做的 - 它在内部维护零个或一个子控件 ( EditingControl),并且当没有此类控件处于活动状态时,它通过返回 来处理许多键(导航、制表符、输入、转义等)true,从而防止子TextBox键盘事件生成。返回值由方法控制ProcessDataGridViewKey。
由于该方法是virtual,因此您可以将 替换DataGridView为自定义派生类,该派生类将重写上述方法,并在视图和视图活动编辑器(如果有)都没有键盘焦点时防止出现不良行为。
像这样的东西:
public class CustomDataGridView : DataGridView
{
bool SuppressDataGridViewKeyProcessing => ContainsFocus && !Focused &&
(EditingControl == null || !EditingControl.ContainsFocus);
protected override bool ProcessDataGridViewKey(KeyEventArgs e)
{
if (SuppressDataGridViewKeyProcessing) return false;
return base.ProcessDataGridViewKey(e);
}
}
Run Code Online (Sandbox Code Playgroud)
以上只是故事的一半,解决了光标导航和选择键的问题。然而DataGridView,拦截另一个关键消息预处理基础设施方法 -Control.ProcessDialogKey并在那里处理Tab、Esc、Return等键。因此,为了防止这种情况,还必须重写该方法并将其重定向到数据网格视图的父级。后者需要一点反射技巧来调用protected方法,但使用一次编译的委托至少可以避免性能损失。
添加后,最终的自定义类将如下所示:
public class CustomDataGridView : DataGridView
{
bool SuppressDataGridViewKeyProcessing => ContainsFocus && !Focused &&
(EditingControl == null || !EditingControl.ContainsFocus);
protected override bool ProcessDataGridViewKey(KeyEventArgs e)
{
if (SuppressDataGridViewKeyProcessing) return false;
return base.ProcessDataGridViewKey(e);
}
protected override bool ProcessDialogKey(Keys keyData)
{
if (SuppressDataGridViewKeyProcessing)
{
if (Parent != null) return DefaultProcessDialogKey(Parent, keyData);
return false;
}
return base.ProcessDialogKey(keyData);
}
static readonly Func<Control, Keys, bool> DefaultProcessDialogKey =
(Func<Control, Keys, bool>)Delegate.CreateDelegate(typeof(Func<Control, Keys, bool>),
typeof(Control).GetMethod(nameof(ProcessDialogKey), BindingFlags.NonPublic | BindingFlags.Instance));
}
Run Code Online (Sandbox Code Playgroud)
小智 3
你可以试试这个。
我创建了自己的文本框并覆盖方法ProcessKeyMessage。
public class MyTextBox : TextBox
{
private const int WM_KEYDOWN = 0x0100;
private const int WM_SYSKEYDOWN = 0x0104;
protected override bool ProcessKeyMessage(ref Message m)
{
if (m.Msg != WM_SYSKEYDOWN && m.Msg != WM_KEYDOWN)
{
return base.ProcessKeyMessage(ref m);
}
Keys keyData = (Keys)((int)m.WParam);
switch (keyData)
{
case Keys.Left:
case Keys.Right:
case Keys.Home:
case Keys.End:
case Keys.ShiftKey:
return base.ProcessKeyEventArgs(ref m);
default:
return base.ProcessKeyMessage(ref m);
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后你可以调用:
var txt = new MyTextBox { Dock = DockStyle.Bottom, BackColor = Color.Khaki };
Run Code Online (Sandbox Code Playgroud)