Combobox-Auswahl einschränken

    • VB.NET

      Combobox-Auswahl einschränken

      Gegeben ein Datenmodell für Bestell-Vorgänge:

      Es gibt Artikel, Bestellungen und BestellPosten. Bestellposten ist Mittler-Tabelle der m:n - Relation zw. den beiden erstgenannten.
      Praktisch bedeutet das, dass in einer Bestellung mehrere Bestellposten enthalten sind, und jeder Bestellposten verweist auf einen Artikel, und gibt die Anzahl an, wieviele dieser Artikel bestellt sind.

      Besonderheit: zusammengesetzter Primärschlüssel
      Bestellpostens Primärschlüssel ist hier interessanterweise keine eigene Tabellenspalte, sondern setzt sich zusammen aus den beiden ForeignKeys zu Artikel und Bestellung.
      Bekanntlich (hoffentlich) muß so ein Primärschlüssel ja eindeutig sein, und bei zusammengesetzten PrimKeys bedeutet das, dass die Kombination beider Fremdschlüssel einzigartig sein muß.
      Praktisch bedeutet das, dass in einer Bestellung derselbe Artikel nicht in 2 verschiedenen Posten auftauchen kann, und das ist hier genau die beabsichtigte Einschränkung.
      Dieses Datenmodell soll verhindern, dass man in einer Bestellung 3 Cola, 1 Fanta und nochmal 1 Cola bestellen kann.
      Stattdessen, wenn der User während des Bestellens merkt, dass 3 Cola zuwenig sind, ist er gezwungen, die Anzahl des bestehenden Cola-BestellPostens einfach erhöhen, statt einen weiteren Cola-BestellPosten hinzuzufügen.

      Useability-Problem im Gui
      Angenommen, man hat eine Bestellung in Ansicht, will einen Posten hinzufügen, und öffnet die entsprechende Combobox zur Auswahl des Artikels. Von Natur aus weiß diese Combo ja nix davon, dass ein Artikel nicht erneut angewählt werden darf, wenn er in dieser Bestellung bereits vorkommt.
      Ein cooles Gui ist also bestrebt, die unerlaubten Artikel aus der Combo zu entfernen, bevor sie geöffnet wird.

      Jo, das habe ich hier mal gemacht. Als Combo habe ich die Combo einer DataGridViewComboboxColumn, und der Zugriff erfolgt im EditingControlShowing - Event des DatagridViews.

      VB.NET-Quellcode

      1. Private Sub PostenDataGridView_EditingControlShowing(ByVal sender As Object, ByVal e As DataGridViewEditingControlShowingEventArgs) Handles PostenDataGridView.EditingControlShowing
      2. If PostenDataGridView.CurrentCell.ColumnIndex <> clmPostenArtikelID.DisplayIndex Then Return 'abbrechen, wenn das EditingControl einer anderen Spalte angehört
      3. Dim cmb = DirectCast(e.Control, ComboBox)
      4. Dim src = ReduceComboColumnDts.Artikel.ToList 'alle Artikel
      5. Dim selItem As ArtikelRow = Nothing
      6. With PostenBindingSource
      7. For i = 0 To .Count - 1
      8. 'diejenigen Artikel rauswerfen, auf die bereits durch andere PostenRows verwiesen wird
      9. Dim art = .At(Of PostenRow)(i).ArtikelRow
      10. If i = .Position Then
      11. selItem = art 'SelectedItem merken
      12. Else
      13. src.Remove(art)
      14. End If
      15. Next
      16. End With
      17. Dim bnds = {cmb.DisplayMember, cmb.ValueMember} 'Member-Einstellungen merken
      18. cmb.DataSource = Nothing 'bisheriges Databinding auflösen
      19. cmb.Items.AddRange(src.ToArray) 'statt binden Items adden
      20. 'Member-Einstellungen restaurieren
      21. cmb.DisplayMember = bnds(0)
      22. cmb.ValueMember = bnds(1)
      23. cmb.SelectedItem = selItem
      24. End Sub
      Das ist ein bischen ein Hack, weil normalerweise ist für die EditingCombo das Databinding eingerichtet entsprechend den im Designer getätigten Einstellungen der DataGridViewComboboxColumn.
      Jetzt aber wird das Databinding aufgelöst, und stattdessen werden die Items der ItemCollection geadded - also aus der gebundenen Combo wird eine ungebundene gemacht.
      Dasselbe nochmal mit Linq ;o)

      VB.NET-Quellcode

      1. Private Sub PostenDataGridView_EditingControlShowing(ByVal sender As Object, ByVal e As DataGridViewEditingControlShowingEventArgs) Handles PostenDataGridView.EditingControlShowing
      2. If PostenDataGridView.CurrentCell.ColumnIndex <> clmPostenArtikelID.DisplayIndex Then Return 'abbrechen, wenn das EditingControl einer anderen Spalte angehört
      3. Dim currPosten = PostenBindingSource.At(Of PostenRow)()
      4. Dim toExcept = From x In PostenBindingSource.All(Of PostenRow)() Where x IsNot currPosten Select x.ArtikelRow
      5. Dim comboSource = srcClmArtikelId.All(Of ArtikelRow).Except(toExcept)
      6. With DirectCast(e.Control, ComboBox)
      7. Dim memberSettings = {.DisplayMember, .ValueMember} 'Member-Einstellungen merken
      8. .DataSource = Nothing 'bisheriges Databinding auflösen
      9. .Items.AddRange(comboSource.ToArray) 'statt Databinding Items adden
      10. 'Member-Einstellungen restaurieren
      11. .DisplayMember = memberSettings(0)
      12. .ValueMember = memberSettings(1)
      13. .SelectedItem = currPosten.ArtikelRow
      14. End With
      15. End Sub


      Zulässige Anzahl Bestellposten beschränken
      Ein weiteres (kleineres) Problem ist, dass bei diesem Datenmodell natürlich keinesfalls mehr Bestellposten einer Bestellung zugefügt werden können, als es Artikel gibt - denn sonst wäre ja einer doppelt aufgeführt.
      Jo, genau diese Aussage kann man im ListChanged-Event der entsprechenden BindingSource quasi in Code gießen, und hat das Problem damit gelöst:

      VB.NET-Quellcode

      1. Private Sub PostenBindingSource_ListChanged(ByVal sender As Object, ByVal e As ListChangedEventArgs) Handles PostenBindingSource.ListChanged
      2. If e.ListChangedType > ListChangedType.ItemDeleted Then Return 'lässt nur .ItemAdded, .ItemDeleted und .Reset passieren
      3. PostenBindingSource.AllowNew = PostenBindingSource.Count < ArtikelBindingSource.Count
      4. End Sub
      Dateien

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