Behandeln Sie Navigationstasten in TextBox innerhalb von DataGridView

Behandeln Sie Navigationstasten in TextBox innerhalb von DataGridView

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 };