DataGridView - Edit Mode kontrollieren

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

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

    DataGridView - Edit Mode kontrollieren

    Hi,

    mein HexEditor funktioniert inzwischen ganz manierlich. Trotzdem bin ich noch nicht ganz zufrieden.

    Die einzelnen Zeichen werden in den Zellen der DatagridView dgvHexDump dargestellt. Jede Zelle kann editiert werden ... dann bin befindet sich diese Zelle im "EditMode".

    Ich möchte als Eingabe nur HexZeichen (also Ziffern und die Buchstaben a - f zulassen. Alle anderen Zeichen sollen ignoriert werden, damit keine "falschen" Eingaben möglich sind.

    Nach dem Drücken der ENTER Taste soll die Eingabe akzeptiert werden.

    Nach dem Drücken der ESC Taste soll die Eingabe verworfen werden (dazu habe ich mir die alten Werte im BeginEdit() Event gemerkt.

    So habe ich das versucht:

    VB.NET-Quellcode

    1. Private Sub dgvHexDump_EditingControlShowing(ByVal sender As Object,
    2. ByVal e As DataGridViewEditingControlShowingEventArgs) _
    3. Handles dgvHexDump.EditingControlShowing
    4. If TypeOf e.Control Is DataGridViewTextBoxEditingControl Then
    5. RemoveHandler DirectCast(e.Control, DataGridViewTextBoxEditingControl).PreviewKeyDown,
    6. AddressOf EditingControlPreviewKeyDown
    7. AddHandler DirectCast(e.Control, DataGridViewTextBoxEditingControl).PreviewKeyDown,
    8. AddressOf EditingControlPreviewKeyDown
    9. End If


    VB.NET-Quellcode

    1. Private Sub EditingControlPreviewKeyDown(ByVal sender As Object, ByVal e As PreviewKeyDownEventArgs)
    2. Debug.WriteLine("Preview Keycode=" & e.KeyCode)
    3. Select Case e.KeyCode
    4. Case Keys.Enter : dgvHexDump.EndEdit() 'ENTER - Accept edited value
    5. Case Keys.Escape 'ESC - Discard edited value
    6. blnRestoreCell = True 'Restore cell flag (honored by EndEdit() )
    7. dgvHexDump.EndEdit() 'Terminate edit mode
    8. blnRestoreCell = False 'Reset flag
    9. Case Else 'Ignore all other non-hex keys
    10. If Not "0123456789ABCDEF".Contains(e.KeyCode.ToString) Then
    11. Debug.WriteLine("Suppress Keycode=" & e.KeyCode.ToString) '<-- how to handle THIS ???
    12. End If
    13. End Select
    14. End Sub


    Die Sache mit ENTER und ESC klappt hervorragend. Der Edit Mode wird in der "richtigen" Weise beendet.

    Aber wie kriege ich das mit den HexZeichen hin ?

    In der Routine dgvHexDump_EditingControlShowing gibt weder e.Handled noch e.SuppressKey etc. Außerdem ist e.KeyCode ReadOnly ... Wie also kann ich "falsche" Zeichen unterdrücken ?

    Im EditingControlShowinggibt es auch nicht gar so viele Events die man abonnieren kann ... Das einzige was ich gefunden habe wäre:

    VB.NET-Quellcode

    1. If TypeOf e.Control Is DataGridViewTextBoxEditingControl Then
    2. RemoveHandler DirectCast(e.Control, DataGridViewTextBoxEditingControl).TextChanged,
    3. AddressOf EditingControlTextChanged
    4. AddHandler DirectCast(e.Control, DataGridViewTextBoxEditingControl).TextChanged,
    5. AddressOf EditingControlTextChanged
    6. End If


    VB.NET-Quellcode

    1. Private Sub EditingControlTextChanged(ByVal sender As Object, ByVal e As EventArgs)
    2. Debug.WriteLine("TextChanged: e=" & e.ToString & " sender=" & sender.ToString)
    3. End Sub


    Aber so richtig brauchbar scheint das auch nicht zu sein ... den geänderten Text kann man sich allenfalls aus dem sender herausschneiden ... und damit kann ich nix verändern.

    Weiß jemand wie ich "falsche" Eingaben im Edit Mode unterdrücken kann ?

    LG
    Peter
    Bilder
    • s 2017-11-30 10-16-053.jpg

      80,34 kB, 916×317, 98 mal angesehen

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

    Erste Frage: Wieso rufst du bei der Esc-Taste ebenfalls "EndEdit" auf und regelst die Wiederherstellung des alten Inhalts so umständlich? Das DataGridView hat eine Methode CancelEdit(), die sollte das alles von ganz alleine erledigen. Und wenn mich nicht alles täuscht, werden EndEdit() und CancelEdit() sogar von Haus aus durch Enter und Esc ausgelöst. Oder täusche ich mich da?
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.
    Zur Abfangen der falsch Eingabe kannst du das KeyPress-Event verwenden.

    VB.NET-Quellcode

    1. Private Sub dgvHexDump_EditingControlShowing(sender As Object, e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles dgvHexDump.EditingControlShowing
    2. RemoveHandler e.Control.KeyPress, AddressOf Control_KeyPress
    3. 'If dgvHexDump.CurrentCell.ColumnIndex = 2 Then
    4. AddHandler e.Control.KeyPress, AddressOf Control_KeyPress
    5. 'End If
    6. End Sub
    7. Private Sub Control_KeyPress(ByVal sender As Object, ByVal e As KeyPressEventArgs)
    8. Select Case True
    9. Case "0123456789ABCDEF".Contains(e.KeyChar.ToString.ToUpper)
    10. e.KeyChar = CChar(e.KeyChar.ToString.ToUpper)
    11. Case Convert.ToInt32(e.KeyChar) = Keys.Back
    12. Case Else
    13. e.Handled = True
    14. End Select
    15. End Sub
    Supi ... das mit dem CancelEdit() macht die Sache natürlich sehr viel einfacher ! Und auch das KeyPress Event (hatte ich übersehen) ist natürlich genau das Richtige. Herzlichen Dank erst mal.

    So sieht mein Coding jetzt aus

    VB.NET-Quellcode

    1. Private Sub EditingControlPreviewKeyDown(ByVal sender As Object, ByVal e As PreviewKeyDownEventArgs)
    2. Debug.WriteLine("Preview Keycode=" & e.KeyCode)
    3. Select Case e.KeyCode
    4. Case Keys.Enter : dgvHexDump.EndEdit() 'ENTER - Accept edited value
    5. Case Keys.Escape : dgvHexDump.CancelEdit() 'ESC - Discard edited value
    6. End Select
    7. End Sub
    8. Private Sub EditingControlKeyPress(ByVal sender As Object, ByVal e As KeyPressEventArgs)
    9. Debug.WriteLine("KeyDown: e=" & e.ToString & " sender=" & sender.ToString)
    10. If e.KeyChar < " "c Then Exit Sub 'Accept control keys (backspace, delete, etc.)
    11. If Not "0123456789ABCDEF".Contains(Char.ToString(e.KeyChar).ToUpper) Then e.Handled = True
    12. End Sub


    Damit verhält sich meine dgv im EditMode jetzt sehr manierlich !

    Escape beendet übrigens NICHT den EditMode von alleine !

    Enter tut das schon .... aber ... dann springt die Auswahl in die nächste Zeile. Und das will ich nicht. Wenn ich also den EditMode mit EndEdit() sozusagen in vorauseilendem Gehorsam beende, dann bleibt die Auswahl da wo sie ist. Und genau das will ich haben.

    Damit wäre ich schon fast glücklich ... aber eine Kleinigkeit bleibt noch ...

    In meiner Zelle stehen jetzt prinzipiell nur gültige HexStrings, denn andere Zeichen kriege ich da nicht mehr hinein !

    Aber, wenn ich etwa mit Backspace alle Zeichen lösche und dann die ENTER Taste drücke, dann würde ich gern CancelEdit() auslösen !

    Weiterhin möchte ich verhindern, dass mehr als ZWEI Zeichen eingeben werden können. Wenn die Zelle bereits zwei Zeichen enthält, dann sollen alle weiteren Eingaben mit Ausnahme von BACKSPACE etc. unterdrückt werden.

    Weiß jemand, wie man das machen könnte ? Ich müsste beispielsweise in der abonnierten KeyDown oder KeyPress Routine den Inhalt oder die Länge der EditZelle abfragen können.

    LG
    Peter

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

    Peter329 schrieb:

    Ich müsste in der abonnierten KeyDown oder KeyPress Routine den Inhalt oder die Länge der EditZelle abfragen können.
    Das in deiner KeyPress-Eventroutine übergebene sender-Objekt sollte eigentlich dein DataGridViewTextBoxEditingControl sein. Wenn du also sender in den Typ DataGridViewTextBoxEditingControl castest, kannst du die Text-Eigenschaft auslesen und auswerten.

    Ich würde aber vermutlich nicht das KeyPress-Ereignis dafür wählen. Wie willst du Sondertasten behandeln? Du müsstest unterscheiden, ob Einfügen oder Kopieren ausgelöst wurde, etc. Da handelst du dir einen riesigen Rattenschwanz an Bedingungen ein, die du prüfen müsstest. Belass es bei der Abfrage der gültigen Zeichen im KeyPress und lass diesen Teil.
    Stattdessen würde ich zusätzlich das TextChanged-Event behandeln. Da dieses erst eintritt, wenn der Text bereits geändert ist, hilft hier ein "Proxy", der gültige Inhalte zwischenspeichert, um zu diesen bei ungültigen Inhalten zurückkehren zu können.

    VB.NET-Quellcode

    1. Private txtLetzterGueltigerText As String
    2. Private Sub EditingControlTextChanged(sender as Object, ...)
    3. Dim control = sender As DataGridViewTextBoxEditingControl
    4. Dim text = control.Text
    5. If IstTextGueltigerHexWert(text) Then
    6. txtLetzerGueltigerText = text
    7. Else
    8. control.Text = txtLetzterGueltigerText
    9. End If
    10. End Sub

    Hier wird angenommen, dass es eine Funktion "IstTextGueltigerHexWert" gibt, die den übergebenen String prüft und z.B. False zurückgibt, wenn im Text für Hex unerlaubte Zeichen vorkommen oder er zu lang ist.

    Prüfe mal in deiner bisherigen Lösung, was passiert, wenn du hier im Browser Strg+A und dann Strg+C drückst, dann zu deiner Anwendung wechselst und dort in deinem DataGrid im EditMode Strg+V drückst. Du wirst staunen, was KeyPress alles NICHT abfangen kann.
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.
    Das mit dem "Probleme einhandeln" kann ich nur bestätigen ! :) Und ansonsten erst mal Danke, dass du dich mit meinem Problem freundlicherweise befasst !

    Also ... ich hab jetzt mal versucht deinen Vorschlag für die "leere" Eingabe umzusetzen:

    VB.NET-Quellcode

    1. Private Sub EditingControlTextChanged(ByVal sender As Object, ByVal e As EventArgs)
    2. Debug.WriteLine("TextChanged: e=" & e.ToString & " sender=" & sender.ToString)
    3. Dim control As DataGridViewTextBoxEditingControl = DirectCast(sender, DataGridViewTextBoxEditingControl)
    4. Dim text As String = control.Text
    5. If text = "" Then control.Text = oldEditValue
    6. End Sub


    Wenn der Inhalt der editierten Zelle "leer" ist, dann wird der Zellen Inhalt restauriert. Den vorangehenden Inhalt oldEditValue habe ich mir im BeginEdit Event gemerkt.

    Das funktioniert so. Aber das ist nicht was ich haben will. Denn sobald die Zelle leer ist, erscheint sofort der ursprüngliche Inhalt.

    Ich möchte aber haben, dass der Inhalt nur restauriert wird, wenn der EditMode VERLASSEN wird. Denn solange ich im EditMode bin, kann der Anwender ja noch Eingaben machen.

    Ich hoffe, ich habe mein Anliegen veständlich machen können !

    LG
    Peter
    Ich meinte ja auch nicht, den ursprünglichen Inhalt zu restaurieren, sondern den letzten bekannten "gültigen" während des Editierens. Ich würde auch "Inhalt = leer, also alten Wert eintragen" gar nicht machen. Zumindest nicht im TextChanged-Ereignis, sondern im CellEndEdit-Ereignis des DGV.
    In TextChanged sorgst du nur dafür, dass ungültiger Text durch den letzten bekannten gültigen Text ersetzt wird und kürzt ggf. einfach zu langen Text auf die gewünschten zwei Zeichen. Wenn man das korrekt implementiert, kann man sich auch die Prüfung auf gültige hexadezimale Zeichen im KeyPress-Event sparen und dort lediglich auf Enter und Cancel reagieren.
    Und ob der (ggf. gekürzte) Text gültig ist, prüfst du einfach, indem du mit .NET-Bordmitteln versuchst, den String aus dem Textfeld als Hexwert zu interpretieren und in eine Ganzzahl umzuwandeln (Convert.ToInt32 oder Integer.TryParse - eins der beiden sollte mit passendem Parameter Hex-Strings umwandeln können).
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.
    ok, ich hab mal versucht deine Ratschläge umzusetzen.

    Dass mit dem "empty Input" ist klar - das verlagere ich ins EndEdit(). Und so funktioniert das auch.

    Schwieriger wird das mit den "zu langen" Eingaben. Denn das muss ich ja unmittelbar abfangen (da ich nicht erst endlose Eingaben entgegen nehmen möchte, nur um sie hinterher abzulehnen). :)

    Dazu nutze ich jetzt das abonniert "TextChanged" Ereignis:

    VB.NET-Quellcode

    1. Private Sub EditingControlTextChanged(ByVal sender As Object, ByVal e As EventArgs)
    2. Debug.WriteLine("TextChanged: e=" & e.ToString & " sender=" & sender.ToString)
    3. Dim control As DataGridViewTextBoxEditingControl = DirectCast(sender, DataGridViewTextBoxEditingControl)
    4. Dim text As String = control.Text 'Get text input
    5. If text.Length <= 2 Then 'Length of input is valid
    6. oldText = text 'Remember input
    7. Else 'Length of input is too long
    8. control.Text = oldText 'Restore previous input
    9. End If
    10. End Sub


    [edit]

    Und so funktioniert das auch !

    Der Text Cursor springt zwar immer an den Anfang der Zelle.

    [edit2]

    Aber auch das kann man ganz einfach beheben: control.select(99, 2)

    Supi ... nach anfänglicher Verwirrung bin ich jetzt rundum zufrieden !

    Herzlichen Dank für deine Ratschläge !

    LG
    Peter

    [edit3]

    Kann man das mit dem Cast noch vermeiden, in dem man andere Parameter vereinbart ?

    VB.NET-Quellcode

    1. Dim control As DataGridViewTextBoxEditingControl = DirectCast(sender, DataGridViewTextBoxEditingControl)

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

    Peter329 schrieb:

    Kann man das mit dem Cast noch vermeiden, in dem man andere Parameter vereinbart ?
    Du bekommst doch das sender-Argument über den Eventhandler, das ist doch der Witz dabei, dass alle Eventhandler so aufgebaut sind, dass im Sender-Argument des Eventauslösers da ein Me bzw. this steht und der Eventempfänger halt den Sender auslesen kann.
    Ggf. ist da noch eine Typabfrage erforderlich, wenn verschiedene Controls oder andere Klassen dieses Event bedienen.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Peter329 schrieb:

    Kann man das mit dem Cast noch vermeiden, in dem man andere Parameter vereinbart ?
    Nein. Der sender-Parameter muss immer object (VB: As Object) sein, sonst stimmt die Deklaration nicht mit dem Delegaten für das Event überein. Solange du das Event nur dem TextEdit-Control des DGV zuordnest, kannst du dir die Prüfung allerdings sparen und direkt casten, so wie du es ja gemacht hast. Das ist der einfachste und direkteste Weg.
    Weltherrschaft erlangen: 1%
    Ist dein Problem erledigt? -> Dann markiere das Thema bitte entsprechend.
    Waren Beiträge dieser Diskussion dabei hilfreich? -> Dann klick dort jeweils auf den Hilfreich-Button.
    Danke.
    ok, ihr beiden, das hatte mir jetzt noch gefehlt. Supi, ich habe einiges gelernt ! Danke!

    @Arby

    Ich hab jetzt meine Edit Mode Routinen durch kodiert und auf Herz und Nieren getestet. Tatsächlich muss man da eine Menge beachten, bis das "anständig" läuft. Offensichtlich hast du damit einige Erfahrung, denn in Nachhinein, kann ich bestätigen, dass deine Hinweise GENAU den richtigen Weg aufgezeigt haben !

    Mein HexEditor läuft Klasse. Ich bin begeistert. Mit relativ wenigen Zeilen Code erhält man eine Routine, die wirklich "Hand und Fuß" hat. Ich will mich ja nicht selber loben aber das ist jetzt der beste HexEditor, den ich je zu Gesicht bekommen habe ! :)

    Herzlichen Dank nochmal an alle und

    LG
    Peter