Nach erfolgter Auswahl die Auswahlliste in einer DataGridViewComboBox ändern

  • VB.NET

Es gibt 37 Antworten in diesem Thema. Der letzte Beitrag () ist von VB1963.

    Nach erfolgter Auswahl die Auswahlliste in einer DataGridViewComboBox ändern

    Hallo Community!

    Wie kann man bei einer ComboBoxColumn die Auswahlliste unmittelbar nach einer getätigten Auswahl per Programm ändern?
    Z.B: Es stehen Zahlen von 1 bis 4 in der Auswahlliste einer ComboboxColumn zur Verfügung
    und nach erfolgter Auswahl einer beliebigen Zahl wird die Auswahlliste um die ausgewählte Zahl reduziert usw.

    Siehe anbei ... , die Liste wird zwar reduziert, aber das DGV überträgt die ausgewählte Zahl nicht in die Zelle ...

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Sub SetUp()
    2. Size = New Size(200, 300)
    3. Dim flwLayout As New FlowLayoutPanel()
    4. With flwLayout
    5. .FlowDirection = FlowDirection.TopDown
    6. .Dock = DockStyle.Fill
    7. .Controls.Add(DGV)
    8. End With
    9. Controls.Add(flwLayout)
    10. Text = "DEMO"
    11. Dim cbxCol As New DataGridViewComboBoxColumn()
    12. With cbxCol
    13. .Items.AddRange(1, 2, 3, 4)
    14. .ValueType = GetType(Int32)
    15. .DisplayStyle = DataGridViewComboBoxDisplayStyle.Nothing
    16. .HeaderText = "Versuch"
    17. End With
    18. With DGV
    19. .AllowUserToAddRows = True
    20. .AllowUserToDeleteRows = True
    21. .Columns.Add(cbxCol)
    22. End With
    23. End Sub
    24. Private Sub dgv_ecs(ByVal sender As Object, ByVal e As DataGridViewEditingControlShowingEventArgs) _
    25. Handles DGV.EditingControlShowing
    26. Dim cbx As ComboBox = CType(e.Control, ComboBox)
    27. If (cbx IsNot Nothing) Then
    28. RemoveHandler cbx.SelectedIndexChanged, New EventHandler(AddressOf cbx_SelIndChg)
    29. AddHandler cbx.SelectedIndexChanged, New EventHandler(AddressOf cbx_SelIndChg)
    30. End If
    31. End Sub
    32. Private Sub cbx_SelIndChg(ByVal sender As Object, ByVal e As EventArgs)
    33. Dim cbx As ComboBox = CType(sender, ComboBox)
    34. cbx.Items.Remove(CType(sender, ComboBox).SelectedItem)
    35. End Sub
    36. End Class

    Wie kann man hier weiter bzw. anders vorgehen?

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

    Geht glaub nicht mit einer DGV-ComboboxColumn.
    Die DataSource einer solchen Column muß alle Werte beinhalten, die in der gesamten Spalte vorkommen. Andernfalls wären bestimmte Zellwerte ungültig, und du kriegst eine Fehlermeldung.

    (Vlt. geht doch was, wenn man die Column ganz ohne Datasource lässt - habich aber k.A. von, und vlt. gehts auch nicht)
    @ErfinderDesRades

    Danke für diesen Aspekt. Ich werde das von dieser Sichtweise einmal ausprobieren.
    Ich glaube, ich hab's ... :)

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Dim intList As New List(Of Integer)
    2. Dim intValue As Integer
    3. Private Sub SetUp()
    4. Size = New Size(200, 300)
    5. Dim flwLayout As New FlowLayoutPanel()
    6. With flwLayout
    7. .FlowDirection = FlowDirection.TopDown
    8. .Dock = DockStyle.Fill
    9. .Controls.Add(DGV)
    10. End With
    11. Controls.Add(flwLayout)
    12. Text = "DEMO"
    13. Dim cbxCol As New DataGridViewComboBoxColumn
    14. With cbxCol
    15. '.Items.AddRange(1, 2, 3, 4)
    16. .ValueType = GetType(Int32)
    17. .DisplayStyle = DataGridViewComboBoxDisplayStyle.Nothing
    18. .HeaderText = "Versuch"
    19. End With
    20. With DGV
    21. .AllowUserToAddRows = True
    22. .AllowUserToDeleteRows = True
    23. .Columns.Add(cbxCol)
    24. End With
    25. intList.Add(1)
    26. intList.Add(2)
    27. intList.Add(3)
    28. intList.Add(4)
    29. End Sub
    30. Private Sub DGV_CellBeginEdit(sender As Object, e As System.Windows.Forms.DataGridViewCellCancelEventArgs) Handles DGV.CellBeginEdit
    31. If (DGV.CurrentCell.ColumnIndex = 0) Then
    32. Dim cbxCol As DataGridViewComboBoxCell = TryCast(sender.rows(e.RowIndex).cells(e.ColumnIndex), DataGridViewComboBoxCell)
    33. With cbxCol
    34. For i = 0 To intList.Count - 1
    35. .Items.Add(intList(i))
    36. Next
    37. End With
    38. End If
    39. End Sub
    40. Private Sub DGV_CellValueChanged(sender As Object, e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DGV.CellValueChanged
    41. If (DGV.CurrentCell.ColumnIndex = 0) Then
    42. intValue = Me.DGV.Rows(e.RowIndex).Cells(e.ColumnIndex).Value
    43. intList.Remove(intValue)
    44. End If
    45. End Sub
    46. Private Sub DGV_DataError(sender As Object, e As System.Windows.Forms.DataGridViewDataErrorEventArgs) Handles DGV.DataError
    47. MessageBox.Show(e.Exception.ToString)
    48. End Sub

    Vielleicht kann man den Code da noch verbessern? ...
    mir ist bisserl abstrus, was du da mittm FlowLayoutPanel veranstaltest - kannst du das nicht einfach ausse Toolbox draufziehen - dann hättest du damit im Code nixmeh zu schaffen.

    auch die ComboColumn würde ich im Designer machen.
    Auch das DataError-Event ist Unfug, denn diese Messagebox kriegste sowieso.

    Also von deim Code bleibt nicht viel übrig, und das ist sehr zu seinem Vorteil:

    VB.NET-Quellcode

    1. Dim intList As New List(Of Integer) From {1, 2, 3, 4}
    2. Private Sub SetUp()
    3. Me.cbxCol.ValueType = GetType(Int32)
    4. End Sub
    5. Private Sub DGV_CellBeginEdit(ByVal sender As Object, ByVal e As DataGridViewCellCancelEventArgs) Handles DGV.CellBeginEdit
    6. If e.ColumnIndex <> 0 OrElse not e.RowIndex.IsBetween(0, DGV.RowCount - 1) Then Return
    7. cbxCol.Items.Clear()
    8. cbxCol.Items.AddRange(intList)
    9. End Sub
    10. Private Sub DGV_CellValueChanged(ByVal sender As Object, ByVal e As DataGridViewCellEventArgs) Handles DGV.CellValueChanged
    11. If (e.ColumnIndex = 0) Then
    12. intList.Remove(CInt(DGV(e.ColumnIndex, e.RowIndex).Value))
    13. End If
    14. End Sub

    Also wenn das funktioniert...

    Edit: Ups - die IsBetween-Extension ist ein Eigenbau. Du müsstest es entweder händisch ausprogrammieren, oder versuchen, diese Extension bei dir einzufrickeln:

    VB.NET-Quellcode

    1. Public Module IComparableX
    2. <Extension()> _
    3. Public Function IsBetween(Of T As IComparable)( _
    4. ByVal ToTest As T, _
    5. ByVal Bord0 As T, _
    6. ByVal Bord1 As T) As Boolean
    7. Return Bord0.CompareTo(ToTest) <= 0 AndAlso ToTest.CompareTo(Bord1) <= 0
    8. End Function


    Edit2:
    Ich werde das von dieser Sichtweise einmal ausprobieren.
    Also das hier ausprobierte ist ühaupt nicht meine sichtweise, um das klarzustellen.
    Ich verwende DGV üblicherweise nur datengebunden an typisierte Datasets - naja, meine Signatur ist glaub voll davon ;).

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

    vielen Dank ErfinderDesRades!

    Da hast du mich aber ganz schön gestutzt (codemäßig) :)

    Deine Ansichten über DGV, Dataset und Co. weis ich zu schätzen und ich habe in deinem Buch schon oft nachgelesen ;)

    Ich werde mir das jetzt in Ruhe anschauen, vielleicht kommen da noch Fragen...

    VB1963 schrieb:

    Deine Ansichten über DGV, Dataset und Co. weis ich zu schätzen und ich habe in deinem Buch schon oft nachgelesen ;)

    Endlich mal ein (dienst-) junger Kollege, der den guten Rat eines (dienst-) älteren Kollegen schätzt.
    @VB1963: Weiter so. :thumbup:
    @EDR: :thumbup:
    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!
    Danke sehr! :)

    Ich habe herausgefunden, dass man im Ereignis BeginEdit eine ComboBoxCell casten muss,
    sonnst funktioniert die Auswahlliste in der Combobox so nicht.
    Wenn ich aber die .addRange-Methode anwende, erscheint nur der Wortlaut 'Auflistung' im Auswahlbereich.
    Da muss ich einzeln adden - dann gehts? Das würde mich sehr interessieren, fehlt da ein Verweis...

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Option Strict On
    2. Public Class Form1
    3. Dim intList As New List(Of Integer) From {1, 2, 3, 4}
    4. Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
    5. SetUp()
    6. End Sub
    7. Private Sub DGV_CellBeginEdit(ByVal sender As Object, ByVal e As DataGridViewCellCancelEventArgs) Handles DGV.CellBeginEdit
    8. Dim cbxCell As DataGridViewComboBoxCell = TryCast(DGV.Rows(e.RowIndex).Cells(e.ColumnIndex), DataGridViewComboBoxCell)
    9. If e.ColumnIndex <> 0 OrElse Not e.RowIndex.IsBetween(0, DGV.RowCount - 1) Then Return
    10. cbxCell.Items.Clear()
    11. cbxCell.Items.AddRange(intList)
    12. 'For i = 0 To intList.Count - 1
    13. ' cbxCell.Items.Add(intList(i))
    14. 'Next
    15. End Sub
    16. Private Sub DGV_CellValueChanged(ByVal sender As Object, ByVal e As DataGridViewCellEventArgs) Handles DGV.CellValueChanged
    17. If (e.ColumnIndex = 0) Then
    18. intList.Remove(CInt(DGV(e.ColumnIndex, e.RowIndex).Value))
    19. End If
    20. End Sub

    Beim Eingeben der Zahlen scheint es keine Probleme zu geben,
    aber beim Löschen oder Überschreiben erscheint dann wieder die
    DataGridView-Ausnahme: "Der DataGridViewComboCell-Wert ist ungültig"
    ...das ergibt dann wieder ein programmtechnisches Weitergefrickel :S

    Da werde ich eine andere Lösung suchen müssen...
    Bilder
    • Auflistung.png

      52,79 kB, 1.232×350, 307 mal angesehen

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

    Jetzt habe ich diese Sache mit der DataGridViewComboBoxColumn doch in dieser Form weiterverfolgt.
    Eine DataGridView-Ausnahme: "Der DataGridViewComboCell-Wert ist ungültig" wird jetzt nicht mehr ausgelöst.
    Ich vermute, dieser Fehler tritt dann nicht auf, wenn der Wert in der aktiven Zelle sich auch zeitgleich
    in der ComboBox-Auswahlliste befindet...

    Folgende Funktionen der ComboBox, wie:
    - Werte auswählen und in leere Zelle übertragen
    - bereits ausgewählte Werte in der gefüllten Zelle überschreiben
    - Zeile löschen
    funktionieren jetzt ohne Fehler.
    Die ComboboxAuswahlliste passt sich jetzt nach jeder Auswahl wertausschließend an,
    damit keine Dublikate in der behandelten DGV-Spalte auftreten können.

    Dieses kleine Demoprojekt benötigt per Designer eine Form mit einer leeren DataGridView (DGV).
    Die DataGridViewComboboxColumn (cbxCol) wird während der Laufzeit generiert.
    Hier kann man codemäßig sicher wieder einiges machen ...


    Hinweis: Eine designergenerierte DataGridViewComboboxColumn löst mir hier beim Starten immer eine InvalidOperationException aus :?:
    Daher generiere ich cbxCol zur Laufzeit
    [VB.NET] Exceptions, und was sie uns sagen wollen
    Dateien
    Jo, funzt doch alles sehr wunnebar.
    Das mit den Fehlern kann daran liegen, dass ein Event, welches per Handles-Klausel abonniert, sofort bereit ist, also bereits in InitializeComponents(), und u.U. bereits auslöst, während die Hälfte von dem anneren Zeugs noch garnet initialisiert ist.

    Problem ist nur, dass noch ganz ohne Databinding gearbeitet wird.
    Also guggemal JoiningView auf 4Views - das stellt ja nochn paar mehr Ansprüche.
    Danke für deine Hinweise!
    Das mit der Databinding habe ich versucht --> keine Chance ohne den Fehler "Der DataGridViewComboCell-Wert ist ungültig" :!:
    wobei ich mir da fast ganz sicher war, dass das mit Databindig funktionieren wird ... nicht typisiertes Dataset :S
    Ich werde mich in typisiertes Dataset und die 4 View's mehr vertiefen müssen ;)
    So, jetzt habe ich noch zum Schluss ein Beispiel mit typisiertes Dataset (DBExtensions) von ErfinderDesRades und ein Danke:)
    ... weniger Codebedarf beim (Re)Load!
    ... da bin ich zwar noch ganz am Anfang, aber in diese Richtung werde ich jetzt weitermachen ^^
    Dateien
    scheint ja zu funzen.

    hast du mal überlegt, von DatagridViewComboboxEditingControl zu erben statt von Combobox?

    Auch scheint mir das mit der Shared cbxList ganz gräuselig.

    Wenn in deine App 2 solche ComboColumns rumfahren - kommen die sich da nicht gegenseitig inne Quere?

    Dabei muß doch möglich sein, die Combo anhand der DGV-Spalte und des DGVs korrekt zu befüllen.
    Danke @EDR:

    Bei der Vererbungsgeschichte habe ich nachgeschaut, da habe ich wohl eine Klasse zu viel erstellt.
    Ich habe das jetzt abgekürzt : Class DepressComboBoxList entfernt bzw. den Umweg eingespart
    und dessen Inhalt in die Class DepressComboBoxListEditingControl hineingestellt,
    die jetzt direkt von Combobox erbt und die Schnittstelle IDataGridViewEditingControl implementiert.
    Hast du das bei der ersten Frage gemeint?

    Das mit der Public Shared strAuswahlListe As New List(Of String), da habe ich selbst ein schlechtes Anfängergefühl.
    Da stimmen deine Befürchtungen zu 100% - soweit habe ich da noch gar nicht weitergedacht!
    Ich habe eine zweite Spalte im DGV angelegt und ausprobiert - und siehe da, die teilen sich jetzt die Listenelemente :pinch:

    Wie soll ich da jetzt weiter vorgehen? :huh:
    ... strAuswahlliste weglassen und die CoboBox direkt befüllen? :whistling:

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

    musste selbst wissen. entweder das ding tut jetzt, wasses für dich soll, oder du willst was allgemeingültiges basteln, welches diese Anforderung "Auswahl mit sukzessiver Reduktion der Optionen" ganz allgemein umsetzt.

    Da wäre ich dafür, es richtig Databindingfähig zu machen, sodass man da nicht nur Zahlen reinmachen kann, sondern dass man richtige JoiningViews damit basteln kann.

    Wenn das von ComboColumn erbt, dann besteht ja die Property DataSource bereits, und dann müssteman halt was listenreiches schreiben, dass sich die Combo mit Items aus der DataSource befüllt, und gleichzeitig DisplayMember, ValueMember, DataPropertyName berücksichtigt.

    Derzeit besteht ja auch eine Art "DataSource" - nämlich eben diese eigenartige cbxList. Aber dassis halt "proprietär", wie man glaub sagt.

    ErfinderDesRades schrieb:

    hast du mal überlegt, von DatagridViewComboboxEditingControl zu erben statt von Combobox?

    das meinte ich übrigens genau wies da steht.

    Indemde von DatagridViewComboboxEditingControl erbst kannstedir das vollständige Implementieren von IDataGridViewEditingControl sparen, denn das wird ja bereits von DatagridViewComboboxEditingControl bedient.
    Sodaß du nur noch bestimmte Methoden ganz gezielt zu überschreiben brauchst.

    Das Teil ist nett - hübsches Lehrstück in angewandtes OOP :thumbsup:
    Hab jetzt eine Simpel-Lösung gefunden:

    VB.NET-Quellcode

    1. Private Sub Article2DataGridView_EditingControlShowing(ByVal sender As Object, ByVal e As DataGridViewEditingControlShowingEventArgs) Handles Article2DataGridView.EditingControlShowing
    2. Dim cmb = TryCast(e.Control, DataGridViewComboBoxEditingControl)
    3. If cmb.Null Then Return
    4. With Me.NorthWindDts
    5. cmb.DataSource = .Kategorien.Except(From rwA In .Article2 Select rwA.KategorienRow).ToArray
    6. End With
    7. End Sub

    Ich denke, weil das so simpel ist, und so selten gefragt, isses doch nicht so sinnvoll, da jetzt was allgemeingültiges auszuarbeiten.
    Hey VB1963 ;)
    meinst du ungefähr so, dass man in der Auswahlliste den Mitarbeiter hat und in der DGV die ihm zugehörigen Kunden?
    Und wenn man dann den Mitarbeiter ändert, erhält man von dem neuem Mitarbeiter die Liste aller ihm zugehörigen Kunden?

    Grüßle Marco