DatagridView im Edit Mode - Ereignisse abfangen

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 26 Antworten in diesem Thema. Der letzte Beitrag () ist von Peter329.

    DatagridView im Edit Mode - Ereignisse abfangen

    Hi,

    mein Problem ist ein bissl kniffelig. Ich hoffe, ihr habt ein wenig Geduld mit mir.

    Ich habe eine Datagridview ... und möchte das Ereignis "Enter Taste wurde gedrückt" verarbeiten.

    Das klingt zunächst ganz einfach: man nehme die Events dgv.KeyDown oder dgv.KeyPress und frage den KeyCode.Enter ab.

    So far so good!

    Nun befindet sich meine dgv aber im edit mode ! Und da werden die Ereignisse dgv.KeyDown und dgv.KeyPress nicht mehr ausgelöst ! Warum ? Ganz einfach, weil im EditMode die dgv Zeile dynamisch durch eine Textbox überlagert wird. Und die kriegt fängt dann KeyDown und KeyPress.

    Die Frage ist also, wie ich an die Ereignisroutinen dieser dynamischen TextBox herankomme. Ansprechen kann ich diese Box:

    VB.NET-Quellcode

    1. If Me.dgvEdit.CurrentCell.EditType = GetType(DataGridViewTextBoxEditingControl) Then
    2. With DirectCast(dgvEdit.EditingControl, TextBox)
    3. .SelectionStart = .Text.Length 'Position to end of row ... orelse
    4. .SelectionStart = 0 'Position to start of row
    5. End With
    6. End If


    Das klappt hervorragend ... Nur wie komme ich an die vermaledeiten Event Routinen von dem Dinges heran ?

    Ich hoffe, ich habe mein Anliegen verständlich machen können.

    LG
    Peter

    Peter329 schrieb:

    Nun befindet sich meine dgv aber im edit mode !


    Ein DataGridView ist immer in einem EditMode. DataGridViewEditMode ist eine Property der DataGridView, diese DataGridViewEditModes(Enum) stehen zur verfuegung:
    EditOnEnter
    EditOnKeystroke
    EditOnKeystrokeOrF2
    EditOnF2
    EditProgrammatically

    Welchen Mode meinst du denn?
    And i think to myself... what a wonderfuL World!
    Das hier meine ich:

    VB.NET-Quellcode

    1. Private Sub dgvEdit_CellClickEnter(sender As Object, e As DataGridViewCellEventArgs) Handles dgvEdit.CellClick,
    2. dgvEdit.CellEnter
    3. Debug.Print("dgvEdit CellClick or CellEnter Event")
    4. CurrentRow = e.RowIndex 'Get current row number
    5. If e.ColumnIndex = 2 Then '"Text" column clicked
    6. dgvEdit.BeginEdit(selectAll:=False) 'Start edit, dont select text


    In diesem Modus kann man den Inhalt der Zelle überschreiben ... und das ist es, was ich hier benötige.

    Und wenn ich mit dem Ändern der Zelle fertig bin, dann möchte ich mit der "Enter" Taste den edit mode beenden, damit die Änderungen in die dgv und die darunter liegende DataTable übernommen werden.

    Und dazu muss ich das Event KeyDown oder KeyPress abfangen ... Aber die dgv feuert in diesem Modus nicht. Sondern dieses Event landet bei der temporären Text Box ... und an dieses Event möchte ich jetzt heran kommen.

    Jetzt klar, wo ich herum turne ? :)

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Peter329“ ()

    Das ist schon klar, aber welchen DataGridViewEditMode hast du eingestellt.
    Irgendwo hast du doch die Property gesetzt. Code oder Designer? Welchen Mode.

    Also ich wuerd hier das Event abbonieren, so z.B.:

    VB.NET-Quellcode

    1. Private Sub DataGridView1_EditingControlShowing(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles DataGridView1.EditingControlShowing
    2. If TypeOf e.Control Is DataGridViewTextBoxEditingControl Then
    3. RemoveHandler DirectCast(e.Control, DataGridViewTextBoxEditingControl).KeyDown, AddressOf EditingControlKeyDown
    4. AddHandler DirectCast(e.Control, DataGridViewTextBoxEditingControl).KeyDown, AddressOf EditingControlKeyDown
    5. End If
    6. End Sub
    7. Private Sub EditingControlKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
    8. End Sub
    And i think to myself... what a wonderfuL World!

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Eddy“ ()

    Also erst mal Danke, dass du dich mit meinem Problem beschäftigst und ganz offensichtlich verstehst, was ich hier treibe ! :)

    Der EditMode der dgv steht auf dem Default im Designer: EditOnKeystrokeOrF2

    Dein Ansatz mit dem Abbonieren des Events klingt mir sehr vielversprechend. Ich hab das jetzt eingebaut.

    VB.NET-Quellcode

    1. Private Sub dgvEdit_EditingControlShowing(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles dgvEdit.EditingControlShowing
    2. If TypeOf e.Control Is DataGridViewTextBoxEditingControl Then
    3. RemoveHandler DirectCast(e.Control, DataGridViewTextBoxEditingControl).KeyDown, AddressOf EditingControlKeyDown
    4. AddHandler DirectCast(e.Control, DataGridViewTextBoxEditingControl).KeyDown, AddressOf EditingControlKeyDown
    5. End If
    6. End Sub
    7. Private Sub EditingControlKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
    8. Debug.Print("Editing Control KeyDown KeyCode=" & e.KeyCode.ToString)
    9. End Sub


    Leider feuert die Enter Taste immer noch nicht.

    Ich hab dann mal den EditMode der dgv probehalber auf EditMode=EditProgrammatically eingestelt. Aber auch das hilft nicht weiter.

    LG
    Peter


    [edit]

    Ich habe noch ein bissl getestet:

    Das Drücken der Enter Taste positioniert den Text Cursor auf die nächste Zeile in der dgv. Das ist wohl eine Standard Funktion.

    Wenn ich aber in der Text Zeile irgendwelche ZEICHEN eingebe (z.B. "a"), dann löst dies das Event KeyDown aus ! Das ist doch schon mal was. Und dieses Verhalten ist unabhängig davon ob der EditMode auf "EditOnKeystrokeOrF2" oder auf "EditProgramatically" steht.

    Ob das jetzt weiter hilft ?


    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „Peter329“ ()

    So, ich glaube ich hab was du moechtest. Im PreviewKeyDown kannst du das Enter bekommen, denke daran das der Focus im EditControl sein muss.

    VB.NET-Quellcode

    1. Private Sub DataGridView1_EditingControlShowing(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles DataGridView1.EditingControlShowing
    2. If TypeOf e.Control Is DataGridViewTextBoxEditingControl Then
    3. RemoveHandler DirectCast(e.Control, DataGridViewTextBoxEditingControl).PreviewKeyDown, AddressOf EditingControlPreviewKeyDown
    4. AddHandler DirectCast(e.Control, DataGridViewTextBoxEditingControl).PreviewKeyDown, AddressOf EditingControlPreviewKeyDown
    5. End If
    6. End Sub
    7. Private Sub EditingControlPreviewKeyDown(ByVal sender As Object, ByVal e As PreviewKeyDownEventArgs)
    8. Debug.WriteLine(e.KeyCode)
    9. End Sub
    And i think to myself... what a wonderfuL World!
    Wow ! Das PreviewKeyDown Event schnackelt jetzt, wenn man die ENTER Taste drückt ! Klasse ! Erst mal herzlichen Dank !

    Ich wäre jetzt ja fast lückenlos happy, aber leider gibt es noch einen Wermutstropfen:

    Nach wie vor wird durch das Drücken der Enter Taste der Text Cursor auf die nächste Zeile gestellt. Warum auch sollte sich durch das Abfangen des PreviewKeyDown Events daran etwas geändert haben. Ich möchte aber erreichen, dass der Text Cursor da stehen bleibt wo er steht. Ich hab deshalb mal versucht

    VB.NET-Quellcode

    1. e.Handled = True


    Aber leider ist e.Handled keine Eigenschaft von PreviewKeyDownEventArgs. Und ich sehe auch nix, was dafür sonst in Frage kommen könnte.

    2. Die Taste RETURN und ENTER verhalten sich genau gleich. D.h. sie liefern beide e.KeyCode = KeyCode.Enter (Keycode=13). Ich würde die beiden Tasten aber gern unterscheiden. RETURN sollte nämlich genau das tun was es jetzt auch schon macht, nämlich den Text Cursor auf die nächste Zeile positionieren. Das müsste doch eigentlich zu machen sein, denn das Keyboard sendet mit Sicherheit verschiedene Werte für die beiden Tasten RETURN und ENTER !

    LG
    Peter
    Also damit Cursor da bleibt wo er ist reicht dies:

    VB.NET-Quellcode

    1. Private Sub DataGridView1_KeyDown(ByVal sender As Object, ByVal e As KeyEventArgs) Handles DataGridView1.KeyDown
    2. If e.KeyCode = Keys.Enter Then
    3. e.SuppressKeyPress = True
    4. End If
    5. End Sub


    Zu 2. da steh ich grad auch auf dem Schlauch.
    And i think to myself... what a wonderfuL World!
    Die Sache mit dem e.SuppressKeyPress funzt aber nur in der dgv ! Ich brauch das im "EditMode" ... und da gibt es diese Eigenschaft leider nicht !

    Hier mein Coding:

    VB.NET-Quellcode

    1. Private Sub EditingControlPreviewKeyDown(ByVal sender As Object, ByVal e As PreviewKeyDownEventArgs)
    2. Debug.Print("Editing Control PreviewKeyDown KeyCode=" & e.KeyCode.ToString)
    3. 'We are in edit mode
    4. If e.KeyCode = Keys.Enter Then
    5. dgvEdit.EndEdit()
    6. dgvEdit.BeginEdit(selectAll:=False) 'Start edit, dont select text
    7. 'e.SuppressKeyPress = True '<-- kein Member von PreviewKeyDownEventArgs
    8. ProcessEnter()
    9. End If
    10. End Sub
    11. Private Sub dgvEdit_KeyDown(sender As Object, e As KeyEventArgs) Handles dgvEdit.KeyDown
    12. Debug.Print("dgvEdit KeyDownKeyCode=" & e.KeyCode.ToString)
    13. 'We are in non-edit mode
    14. If e.KeyCode = Keys.Enter Then
    15. e.SuppressKeyPress = True
    16. ProcessEnter()
    17. End If
    18. End Sub
    19. Private Sub dgvEdit_CellClickEnter(sender As Object, e As DataGridViewCellEventArgs) Handles dgvEdit.CellClick,
    20. dgvEdit.CellEnter
    21. Debug.Print("dgvEdit CellClick or CellEnterEvent")
    22. CurrentRow = e.RowIndex 'Get current row number
    23. If e.ColumnIndex = 2 Then 'Text column clicked
    24. dgvEdit.BeginEdit(selectAll:=False) 'Start edit, dont select text
    25. If Me.dgvEdit.CurrentCell.EditType = GetType(DataGridViewTextBoxEditingControl) Then
    26. With DirectCast(dgvEdit.EditingControl, TextBox)
    27. '.SelectionStart = .Text.Length 'Position to end of row
    28. .SelectionStart = 0 'Position to end of row
    29. End With
    30. End If
    31. End If
    32. End Sub


    An und für sich läuft die Sache schon ganz annehmbar ... aber der Vorschub des TextCursor und die fehlende Unterscheidung von RETURN und ENTER sind schon sehr unangenehm.

    Das ist schon eine ganz schön vertrackte Angelegenheit ...

    LG
    Peter

    VB1963 schrieb:

    (vlt. hilft's)


    Japp, direkt mal ausprobiert.

    VB.NET-Quellcode

    1. Public Class Form1
    2. Const WM_KEYDOWN As Integer = &H100
    3. Const KF_EXTENDED = &H1000000
    4. Private Ex As Boolean
    5. Protected Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Message, ByVal keyData As System.Windows.Forms.Keys) As Boolean
    6. If msg.Msg = WM_KEYDOWN AndAlso msg.WParam.ToInt32 = Keys.Return Then
    7. Ex = ((msg.LParam.ToInt32() And KF_EXTENDED) = KF_EXTENDED)
    8. End If
    9. Return MyBase.ProcessCmdKey(msg, keyData)
    10. End Function
    11. Private Sub Form1_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
    12. If Ex Then
    13. Debug.WriteLine("Enter")
    14. Else
    15. Debug.WriteLine("Return")
    16. End If
    17. End Sub
    18. End Class



    @Peter329
    Wegen dem anderen schau ich spaeter auch noch mal.
    And i think to myself... what a wonderfuL World!
    Hi VB1963,

    das klingt ja gar nicht so übel.

    Ich hab mal (nach bestem Wissen und Gewissen) die Routine von C# nach VB.Net umgesetzt ...

    ... zurückgezogen, weil ich gerade sehe, dass Eddy das freundlicherweise sehr viel besser erledigt hat. Danke ! :)

    [edit]


    Jau, das funktioniert ja hervorragend ! Super, man kann also ENTER und RETURN unterscheiden. Ich bin begeistert.

    Allerdings gibt es im Zusammenhang mit meinem Problem noch einen kleinen Haken:

    VB.NET-Quellcode

    1. Private Sub EditingControlPreviewKeyDown(ByVal sender As Object, ByVal e As PreviewKeyDownEventArgs)
    2. 'We are in edit mode
    3. If Ex Then
    4. Debug.Print("Edit Mode Me.KeyDown Enter")
    5. ...
    6. Else
    7. Debug.Print("Edit Mode Me.Keydown Return")
    8. ...
    9. End If


    Blöde ist nur, dass das Ereignis EditingControlPreviewKeyDown( VOR der Funktion ProcessCmdKey ausgewertet wird.

    Mit anderen Worten, die Erkennung, ob es sich um RETURN oder ENTER handelt kommt zu spät ! Die Routine reagiert immer auf die vorangehende Eingabe ! Und damit scheitert die Sache.

    Uff .... das ist schon eine super zähe Kiste ! Ich hoffe, ihr habt noch eine schlaue Idee !

    LG
    Peter

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Peter329“ ()

    @Peter329
    Also, ich hab's bei mir mal so mit Erfolg getestet:

    VB.NET-Quellcode

    1. Private Sub dgvEdit_EditingControlShowing(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles dgvEdit.EditingControlShowing
    2. If Me.dgvEdit.CurrentCell.EditType = GetType(DataGridViewTextBoxEditingControl) Then
    3. With DirectCast(dgvEdit.EditingControl, TextBox)
    4. AddHandler .PreviewKeyDown, AddressOf EditingControlPreviewKeyDown
    5. .SelectionStart = .Text.Length 'Position to end of row ... orelse
    6. .SelectionStart = 0 'Position to start of row
    7. End With
    8. End If
    9. End Sub
    10. Private Sub EditingControlPreviewKeyDown(sender As Object, e As PreviewKeyDownEventArgs)
    11. If Ex Then
    12. Debug.WriteLine("Enter")
    13. Else
    14. Debug.WriteLine("Return")
    15. End If
    16. End Sub
    17. Const WM_KEYDOWN As Integer = &H100
    18. Const KF_EXTENDED = &H1000000
    19. Private Ex As Boolean
    20. Protected Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Message, ByVal keyData As System.Windows.Forms.Keys) As Boolean
    21. If msg.Msg = WM_KEYDOWN AndAlso msg.WParam.ToInt32 = Keys.Return Then
    22. Ex = ((msg.LParam.ToInt32() And KF_EXTENDED) = KF_EXTENDED)
    23. End If
    24. Return MyBase.ProcessCmdKey(msg, keyData)
    25. End Function
    Hi us4711,

    vielen Dank für das Code Beispiel. Das war natürlich ausgesprochen nett von dir !

    Jetzt hab ich aber doch einige Verständnisfragen dazu. Verstehe ich das richtig, dass bei jedem Wechsel in den Edit Mode ein NEUER Handler hinzugefügt wird ?

    VB.NET-Quellcode

    1. Private Sub dgvEdit_EditingControlShowing(ByVal sender As Object,
    2. ByVal e As DataGridViewEditingControlShowingEventArgs) _
    3. Handles dgvEdit.EditingControlShowing
    4. Debug.Print("dgvEdit EditingControlShowing")
    5. If Me.dgvEdit.CurrentCell.EditType = GetType(DataGridViewTextBoxEditingControl) Then
    6. With DirectCast(dgvEdit.EditingControl, TextBox)
    7. AddHandler .PreviewKeyDown, AddressOf EditingControlPreviewKeyDown
    8. .SelectionStart = .Text.Length 'Position to end of row ... orelse
    9. .SelectionStart = 0 'Position to start of row
    10. End With
    11. End If
    12. End Sub
    13. Private Sub EditingControlPreviewKeyDown(sender As Object, e As PreviewKeyDownEventArgs)
    14. If Ex Then
    15. Debug.Print("EditingControlPreviewKeyDown Enter - Process ENTER command ...")
    16. Else
    17. Debug.Print("EditingControlPreviewKeyDown Return - Process RETURN command ...")
    18. End If
    19. End Sub


    Damit erhalte ich das folgende Testergebnis:

    Quellcode

    1. CellClickEnter Col=2 - Switch to edit mode
    2. dgvEdit EditingControlShowing
    3. >>> ENTER
    4. EditingControlPreviewKeyDown Return - Process RETURN command ...
    5. ProcessCmdKey --> ENTER key was depressed
    6. CellClickEnter Col=2 - Switch to edit mode
    7. dgvEdit EditingControlShowing
    8. >>> ENTER
    9. EditingControlPreviewKeyDown Enter - Process ENTER command ...
    10. EditingControlPreviewKeyDown Enter - Process ENTER command ...
    11. ProcessCmdKey --> ENTER key was depressed
    12. CellClickEnter Col=2 - Switch to edit mode
    13. dgvEdit EditingControlShowing
    14. >>> RETURN
    15. EditingControlPreviewKeyDown Enter - Process ENTER command ...
    16. EditingControlPreviewKeyDown Enter - Process ENTER command ...
    17. EditingControlPreviewKeyDown Enter - Process ENTER command ...
    18. ProcessCmdKey --> RETURN key was depressed
    19. CellClickEnter Col=2 - Switch to edit mode
    20. dgvEdit EditingControlShowing


    Nach jedem Event feuern dann alle Handler ! Da müsste man doch wohl eine Abfrage einbauen, ob es den Handler schon gibt ? Oder den Handler in der Load Prozedur einmalig einrichten ?

    Außerdem sieht man an der Testausgabe, dass das Preview Ereignis VOR dem Aufruf von ProcessCmdKey ausgeführt wird.

    Ich hoffe, dass ich alles richtig verstanden habe.

    LG
    Peter

    Peter329 schrieb:

    Oder den Handler in der Load Prozedur einmalig einrichten ?

    Genau das kann nicht geschehen, da zu diesem Zeitpunkt das EditControl ja noch garnicht existiert. Man müsste sich ein Lösung überlegen, die analog zum EditingControlShowing-Event beim schliessen des EditingControls den Handler für das PreviewKeyDown der gecasteten Textbox (NICHTdes EditingControl) wieder entfernt. Sowas könnte als Basis dienen:

    VB.NET-Quellcode

    1. Private Sub dgvEdit_EditingControlShowing(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles dgvEdit.EditingControlShowing
    2. If Me.dgvEdit.CurrentCell.EditType = GetType(DataGridViewTextBoxEditingControl) Then
    3. With DirectCast(dgvEdit.EditingControl, TextBox)
    4. AddHandler .PreviewKeyDown, AddressOf EditingControlPreviewKeyDown
    5. AddHandler .VisibleChanged, AddressOf TextboxVisibleChanged
    6. .SelectionStart = .Text.Length 'Position to end of row ... orelse
    7. .SelectionStart = 0 'Position to start of row
    8. End With
    9. End If
    10. End Sub
    11. Private Sub TextboxVisibleChanged(sender As Object, e As EventArgs)
    12. With DirectCast(dgvEdit.EditingControl, TextBox)
    13. If .Visible = False Then
    14. RemoveHandler .PreviewKeyDown, AddressOf EditingControlPreviewKeyDown
    15. RemoveHandler .VisibleChanged, AddressOf TextboxVisibleChanged
    16. End If
    17. End With
    18. End Sub
    19. Private Sub EditingControlPreviewKeyDown(sender As Object, e As PreviewKeyDownEventArgs)
    20. If e.KeyCode = Keys.Enter Then
    21. If Ex Then
    22. Debug.WriteLine("Enter")
    23. Else
    24. Debug.WriteLine("Return")
    25. End If
    26. End If
    27. End Sub
    28. Const WM_KEYDOWN As Integer = &H100
    29. Const KF_EXTENDED = &H1000000
    30. Private Ex As Boolean
    31. Protected Overrides Function ProcessCmdKey(ByRef msg As System.Windows.Forms.Message, ByVal keyData As System.Windows.Forms.Keys) As Boolean
    32. If msg.Msg = WM_KEYDOWN AndAlso msg.WParam.ToInt32 = Keys.Return Then
    33. Ex = ((msg.LParam.ToInt32() And KF_EXTENDED) = KF_EXTENDED)
    34. End If
    35. Return MyBase.ProcessCmdKey(msg, keyData)
    36. End Function

    Dabei fiel auf, das eine Auswertung auf Keys.Enter in der EditingControlPreviewKeyDown-Routine fehlte.
    Ich habe nun mal einige Sachen probiert(abgeleitete DGV) vom WndProc bis zum ProcessDataGridViewKey, nichts brachte erloesung. Das einzige was auch im Editiermodus funktioniert hat ist ProcessCmdKey.

    Kann man irgendwie ein eigenes EditingControl nutzen? Die Property EditingControl ist ja ReadOnly, wie koennte man da etwas machen, ich wuerde gern versuchen im eigenen EditingControl ProcessCmdKey zu nutzen.

    Auch mit GetKeyState und GetAsyncKeyState laesst sich hier nichts machen.
    And i think to myself... what a wonderfuL World!

    Eddy schrieb:

    Kann man irgendwie ein eigenes EditingControl nutzen?

    Klar, Du kannst eine eigen DataGridViewColumn erstellen, und in diesem Prozess Dein eigenes Editing-Control definieren. Als Beispiel:
    Gewusst wie: Hosten von Steuerelementen in DataGridView-Zellen in Windows Forms