DataGridView - einzelne Zellen als ComboBox darstellen

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

Es gibt 3 Antworten in diesem Thema. Der letzte Beitrag () ist von VaporiZed.

    DataGridView - einzelne Zellen als ComboBox darstellen

    Hallo zusammen,

    in einem DataGridView soll eine Spalte als ComboBoxColumn definiert werden. Allerdings finde ich es sehr hässlich, dass dabei in jeder einzelnen Zeile die ComboBox als Element angezeigt wird (Column 4 im Screenshot). Ich habe daher nach einem Weg gesucht, nur die gerade aktive Zelle als ComboBox darzustellen und bin schließlich bei der Lösung gelandet, im CellEnter- und CellLeave-Ereignis die jeweilige Zelle als DataGridViewComboBoxCell bzw. als DataGridViewTextBoxCell zu definieren. Das klappt grundsätzlich auch (Column 3). Allerdings tritt dabei in einer Konstellation ein Fehler auf ("Der Vorgang ist ungültig, da er einen Wiedereintrittsaufruf an die SetCurrentCellAddressCore-Funktion zur Folge hat."). Dieser Fehler tritt ausschließlich dann auf, wenn ich mich in die Zeile bewege, die denselben Index wie die betroffene Spalte hat. Also wenn wie in meinem Beispielprojekt in Spalte 3 die ComboBoxCell definiert werden soll, dann tritt der Fehler in Zeile 3 auf. Will ich die ComboBox stattdessen in Spalte 4 definieren, kommt der Fehler, sobald ich Zeile 4 betreten will.

    Auch wenn mich schon interessieren würde, was genau da intern schief läuft, würde mir notfalls auch ein alternativer Weg reichen, wie ich das beschriebene Ziel (immer nur die aktuell ausgewählte Zelle als ComboBox darstellen) erreichen kann :)

    Ich nutze VS Studio 2019 Community Edition, installierte .NET-Version 4.8. Unten ein simples Beispielprojekt zum Nachvollziehen - auf der Form befindet sich nur ein DataGridView mit dem Namen grd.

    Vielen Dank schon mal vorab!

    Matthias


    VB.NET-Quellcode

    1. Public Class Form1
    2. Private Sub grd_CellEnter(sender As Object, e As DataGridViewCellEventArgs) Handles grd.CellEnter
    3. If grd.CurrentCell.ColumnIndex = 2 Then
    4. On Error Resume Next
    5. grd.Rows(grd.CurrentCell.RowIndex).Cells(grd.CurrentCell.ColumnIndex) = New DataGridViewComboBoxCell
    6. If Err.Number > 0 Then Debug.Print(Err.Description)
    7. On Error GoTo 0
    8. End If
    9. End Sub
    10. Private Sub DataGridView1_CellLeave(sender As Object, e As DataGridViewCellEventArgs) Handles grd.CellLeave
    11. On Error Resume Next
    12. If grd.CurrentCell.ColumnIndex = 2 Then grd.Rows(grd.CurrentCell.RowIndex).Cells(grd.CurrentCell.ColumnIndex) = New DataGridViewTextBoxCell
    13. On Error GoTo 0
    14. End Sub
    15. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
    16. grd.Columns.Add("Col1", "Text 1")
    17. grd.Columns.Add("Col2", "Text 2")
    18. grd.Columns.Add("Col3", "Text/Combo 3")
    19. grd.Columns.Add(New DataGridViewComboBoxColumn With {.Name = "Col4", .HeaderText = "Combo 4"})
    20. grd.Rows.Add("1", "Test 1")
    21. grd.Rows.Add("2", "Test 2")
    22. grd.Rows.Add("3", "Test 3")
    23. grd.Rows.Add("4", "Test 4")
    24. grd.Rows.Add("5", "Test 5")
    25. grd.Rows.Add("6", "Test 6")
    26. grd.Rows.Add("7", "Test 7")
    27. grd.Rows.Add("8", "Test 8")
    28. End Sub
    29. End Class
    Bilder
    • DGV_ComboBox.jpg

      25,53 kB, 470×272, 77 mal angesehen
    Hallo,
    da ich das gleiche Problem hatte, bin ich auf diese Seite gestoßen. Deine Erklärung "nur bei Spaltenindex = Zeilenindex" trifft auch genauso bei mir zu.
    Das muss dann wohl ein Fehler in VB sein.
    Ich habe das für mich folgendermaßen gelöst:
    Beim CellEnter Event frage ich ab, ob ich in einer Zelle bin, die ich mit einer Combobox versehen möchte.
    Wenn ja, erfolgt die Abfrage, ob Zeilenindex = Spaltenindex.
    Wenn ja, füge ich an Position 0 eine neue Row ein, die ich mit Daten fülle und auf not visible setze(und/oder noch in einer Zelle mit besonderen Daten kennzeichne). Dadurch verschiebt sich der Rowindex der Zelle, so dass nun die Zelle als Combobox dargestellt werden kann. Wenn ich dann auf meinem Button "Speichern" klicke, lösche ich zuerst alle Rows mit not visible(nicht im DataGridView, sondern im gebundenen Datatable). Durch das Setzen auf not visible erkennst du optisch rein gar nichts von dieser Aktion.
    Beim CellLeave Event wandle ich die ComboboxCell wieder in TextboxCell um.
    Hier der Codeauszug am Anfang von CellEnter. Ich weiß nicht, wie ich den Quellcode so schön hier rein bekomme, deshalb leider nur so:

    VB.NET-Quellcode

    1. 'CellEnter.
    2. Dim ZellTyp As Type
    3. Dim dropdown As DataGridViewComboBoxCell
    4. ZellTyp = DGV.CurrentCell.GetType
    5. If ZellTyp.Name = "DataGridViewComboBoxCell" Then
    6. ' Zelle ist schon ComboBox, raus hier
    7. Exit Sub
    8. End If
    9. If DGV.Columns(DGV.CurrentCell.ColumnIndex).Name = "Unternehmen" Or
    10. DGV.Columns(DGV.CurrentCell.ColumnIndex).Name = "Besitzer" Or
    11. DGV.Columns(DGV.CurrentCell.ColumnIndex).Name = "Bank" Then
    12. If DGV.CurrentCell.RowIndex = DGV.CurrentCell.ColumnIndex Then
    13. ' Fehler in VS !!! Bei Rowindex = Columnindex
    14. ' einfach einen neue Row an Stelle 0 einfügen. Dann kann die Combobox dargestellt werden.
    15. Dim newRow As DataRow = mDTdata.NewRow
    16. ' ID merken wegen Identitätsspezifikation in der Datenbank, zählt automatisch um 1 hoch
    17. Dim merkid As Long = newRow.Item("ID")
    18. ' neue Row mit den Daten de aktuellen füllen(wegen not Null allowed u.s.w.)
    19. newRow.ItemArray = mDTdata.Rows(DGV.CurrentCell.RowIndex).ItemArray
    20. ' als eingefügte Row kenntlich machen, hier ein stringfeld dafür missbrauchen
    21. newRow.Item("Unternehmen") = "Eingefügt"
    22. ' die gemerkte ID eintragen
    23. newRow.Item("ID") = merkid
    24. ' am Anfang der Tabelle einfügen
    25. mDTdata.Rows.InsertAt(newRow, 0)
    26. ' und row auf nicht sichtbar
    27. DGV.Rows(0).Visible = False
    28. End If
    29. End


    CellLeave:

    VB.NET-Quellcode

    1. Dim Zelltyp As Type = DGV.CurrentCell.GetType
    2. If Zelltyp.Name = "DataGridViewComboBoxCell" Then
    3. ' Zelle ist ComboBox, gewählten Eintrag als TextBox wieder mit Name speichern
    4. Dim merk As String = DGV.Rows(DGV.CurrentCell.RowIndex).Cells(DGV.CurrentCell.ColumnIndex).Value
    5. Dim TextBoxCell As New DataGridViewTextBoxCell
    6. TextBoxCell.Value = merk
    7. DGV.Rows(e.RowIndex).Cells(e.ColumnIndex) = TextBoxCell


    Und dann beim Speichern:

    VB.NET-Quellcode

    1. ' ev. die ersten Rows mit "Unternehmen" = "Eingefügt" wieder löschen
    2. ' oder alle, die visible = False haben
    3. For i = DGV.Rows.Count - 1 To 0 Step -1
    4. If DGV.Rows(i).Visible = False Or DGV.Rows(i).Cells("Unternehmen").Value = "Eingefügt" Then
    5. mDTdata.Rows(i).Delete() ' an DGV gebundenes Datatable
    6. End If
    7. Next

    Ich hoffe, dass ich bei dem alten Problem helfen konnte.

    CodeTags gesetzt ~VaporiZed

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

    Hallo

    Ich verwende ein strukturierte Datasets - Da kann ich einfach alles einfach im Designer anpassen, auch ob man eine Textbox oder eine Combobox verwenden will.

    Hier ist ein guter Link dazu..https://www.vb-paradise.de/index.php/Thread/94955-die-vier-Views-auf-Video/

    Am einfachsten mal den 1. Film anschauen.

    LG Panter

    Die tDS-Verwendung löst das hier behandelte Problem nicht. Es ist keine Frage der Datenhaltung, sondern des GUI-Designs. Es geht darum, die ComboBox-Spalte nicht als solche zu erkennen. Sondern erst, wenn man einen Wert auswählen will.
    Und das Problem lässt sich super einfach lösen, zumindest meines Erachtens, auch wenn es den TE nicht mehr interessieren dürfte, aber vielleicht @VB Rentner: Man setzt den DisplayStyle der ComboBox-Spalte auf Nothing. Danach sieht es zur Laufzeit so aus:
    Bilder
    • Invisible ComboBox.gif

      117,88 kB, 552×240, 47 mal angesehen
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.