Anscheinend liegt das Problem in DataGridView
. Weil DataGridView
überschreibt den Control.ProcessKeyPreview
Methode:
Die DataGridView
Implementierung tut genau das - sie verwaltet intern null oder ein untergeordnetes Steuerelement (EditingControl
), und wenn kein solches Steuerelement aktiv ist, verarbeitet es viele Tasten (Navigation, Tab, Enter, Escape usw.) durch Rückgabe von true
, wodurch das Kind TextBox
verhindert wird Generierung von Tastaturereignissen. Der Rückgabewert wird durch ProcessDataGridViewKey
gesteuert Methode.
Da die Methode virtual
ist , können Sie den DataGridView
ersetzen mit einer benutzerdefinierten abgeleiteten Klasse, die die oben genannte Methode überschreibt und das unerwünschte Verhalten verhindert, wenn weder die Ansicht noch der aktive Ansichtseditor (falls vorhanden) den Tastaturfokus hat.
Etwa so:
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);
}
}
Das Obige ist nur die Hälfte der Geschichte und löst das Problem der Cursornavigation und der Auswahltasten. Jedoch DataGridView
fängt eine weitere wichtige Infrastrukturmethode zur Vorverarbeitung von Nachrichten ab - Control.ProcessDialogKey
und verarbeitet Tab , Esc , Zurück , etc. Schlüssel dort. Um dies zu verhindern, muss die Methode ebenfalls überschrieben und auf die übergeordnete Ansicht der Datengitteransicht umgeleitet werden. Letzteres erfordert ein wenig Reflexionstrick, um eine protected
aufzurufen -Methode, aber die Verwendung eines einmal kompilierten Delegaten vermeidet zumindest den Leistungseinbruch.
Mit diesem Zusatz würde die endgültige benutzerdefinierte Klasse wie folgt aussehen:
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));
}
Sie können dies versuchen.
Ich habe mein eigenes Textfeld erstellt und die Methode ProcessKeyMessage überschrieben .
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);
}
}
}
Und dann können Sie anrufen:
var txt = new MyTextBox { Dock = DockStyle.Bottom, BackColor = Color.Khaki };