interaktion mehrerer DataTables / zeigen von offenen Kundenbestellungen

  • VB.NET

Es gibt 36 Antworten in diesem Thema. Der letzte Beitrag () ist von DerSmurf.

    interaktion mehrerer DataTables / zeigen von offenen Kundenbestellungen

    Hallo
    Ich habe gerade etwas programmiert, bzw. bin dabei. Wenn ich mir das aber so anschaue, glaube ich nicht, dass ich den besten Ansatz gewählt habe.
    Folgendes ist gegeben:
    Ein DataSet mit 3 DataTables (CustomerOrder, Order und Supplier)
    Die beiden Tables CustomerOrder und Order haben eine Spalte SupplierID, mit dieser wird auf den entsprechenden LIeferanten verwiesen.
    Nun möchte ich beim tätigen einer neuen Bestellung (Einfügen von neuer Row in Order, mit auswählbarem Supplier), offene Kundenbestellungen dieses "Suppliers" anzeigen.
    Die DataTable CustomerOrder hat folgende relevante Spalten (Date = Bestelldatum des Kunden bei mir / OrderDate = mein Bestelldatum bei der Firma)
    Löse ich nun eine neue Bestellung (Order) aus, suche ich mit einer Schleife in meiner CustomerOrder nach offenen Kunenbestellungen für diese Firma.
    Offen ist eine Kundenbestellung, wenn die Spalte OrderDate DBNull ist.

    VB.NET-Quellcode

    1. Private Function SearchCustomerOrders(SupplierID As Integer) As List(Of String)
    2. Dim OpenOrders As New List(Of String)
    3. Dim COrderRow = DtsSettings.CustomerOrder
    4. If COrderRow.Rows.Count < 1 Then Exit Function
    5. For Each row In COrderRow
    6. If row.IsOrderedNull AndAlso row.SupplierID = SupplierID Then
    7. OpenOrders.Add(row.OrderDate & "|" & row.CustomerName & "|" & row.ArtNr & "|" & row.ArticleName & "|" & row.Amount & "|" & row.ID)
    8. End If
    9. Next
    10. Return OpenOrders
    11. End Function


    Diese Funktion funktioniert, aber gibt es hier nicht eine Möglichkeit der Sortierung (leer nach oben oder sowas), damit ich nicht immer die gesamte DataTable durchforsten muss?

    In der Aufrufsub wird dann geprüft ob die übergebene List(of String) leer ist.
    Wenn nicht, wird eine Form mit DGV angezeigt und die List(of String) an diese übergeben.
    Hier splitte ich die List of wieder und stelle die Daten im Dgv dar.
    Möchte ich nun eine Kundenbestellung als "bestellt" markieren, lese ich aus der List(of string) die ID, gehe mit einer Schleife jede ID in CustomerOrder durch, bis beide übereinstimmen und setzte den Wert für .OrderDate.

    Das funktioniert, scheint mir aber unnötig kompliziert.

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

    Ein paar Anmerkungen, die Du nach Belieben auch ignorieren kannst:
    • In Zeile#4 arbeitest Du mit Exit Function; an dieser Stelle wird nichts an die aufrufende Prozedur zurückgegeben; darüber sollte Dich der Compiler zur Designzeit informieren. Hast Du alle Warnungen in den Projekteigenschaften -> Kompilieren scharfgestellt und lässt Du alle Warnungen als Fehler anzeigen? An der o.g. Codestelle wäre Return New List(Of String) angebracht.
    • Der Name Deiner Function ist irreführend. z.B. RetrieveOpenOrders oder GetOpenOrdersForSupplier() wären m.E. bessere Namen. Die Funktion soll ja keine Bestellungen suchen. Sondern eine Liste von leeren Bestellungen zurückgeben.
    • Warum wird eine ganze Tabelle in eine Variable names COrderRow geschoben? Also, nicht der Grund ist die der Knackpunkt, sondern der Variablenname.
    • Warum heißt es in Z#4 < 1? Da Count nie < 0 sein kann, schreib doch gleich = 0. Allerdings ist die Zeile eh überflüssig.
    • Gib nicht eine List(Of String) zurück. Arbeite mit vereinfachten Tuples:

    VB.NET-Quellcode

    1. Private Function SearchCustomerOrders(SupplierID As Integer) As List(Of (OrderDate As Date, CustomerName As String, ArtNr As Integer, ArticleName As String, Amount As Integer, RowID As Integer))
    2. Dim OpenOrders As New List(Of (Date, String, Integer, String, Integer, Integer))
    3. Dim COrderRow = DtsSettings.CustomerOrder.Where(Function(x) x.SupplierID = SupplierID AndAlso x.IsOrderedNull)
    4. For Each row In COrderRow
    5. OpenOrders.Add((row.OrderDate, row.CustomerName, row.ArtNr, row.ArticleName, row.Amount, row.ID))
    6. Next
    7. Return OpenOrders
    8. End Function
    9. Private Sub ReportAllOpenOrdersForFirstSupplier()
    10. Dim ListOfOpenOrders = SearchCustomerOrders(-1)
    11. For Each OpenOrder In ListOfOpenOrders
    12. MessageBox.Show($"Datum: {OpenOrder.OrderDate}, Kunde: {OpenOrder.CustomerName}, Artikelnummer: {OpenOrder.ArtNr}, Artikel: {OpenOrder.ArticleName}, Anzahl: {OpenOrder.Amount}, Row-ID: {OpenOrder.RowID}")
    13. Next
    14. End Sub

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    Hallo ihr beiden.
    Danke für den Hinweis @petaod. Ich habe erst am Wochenende Zeit mich intensiver mit Linq zu befassen, aber es scheint mir auch an anderer Stelele (Umsätze aus DataSet für ein Jahr - also z.B. 2019 zu suchen) extrem hilfreich zu sein.

    @VaporiZed
    Da du mein Helfer #1 bist, käme ich nie auf die Idee deinen Rat (absichtlich) zu ignorieren.
    (Nichts gegen all die anderen geilen Helfer hier!)
    Ich sortiere deine Punkte mal von oben nach unten mit 1 - 5
    1. Das mit den Warnungen steht auf default. Hab da nie was dran geändert. Mir wird nur die entsprechende Warnung angezeigt.
    Ich arbeite mit Exit Function (auch in anderen Funktionnen), weil ich dachte das sei nicht schlimm.
    Die Funktion rufe ich wie folgt auf:

    VB.NET-Quellcode

    1. Sub Aufruf()
    2. Dim Liste as new List(of string)
    3. Liste = SearchCustomerOrders(Parameter)
    4. If Liste.Count > 0 then 'Kundenbestellungen anzeigen
    5. 'vorherige Variablendeklaration, weil ich die List of String an die, sich öffnende Form, übergebe
    6. End sub

    Mein Gedanke ist, dass ich mit Exit Function ja quasi nichts zurückgebe, die Bedingung der Aurufsub ist also nicht erfüllt.
    Aber Exit Function ist dann ungenau/unsauber? Immer mit Return verlassen?
    Gilt das auch für boolean Funktionen? Sollte ich hier auch Return False, statt exit funtion verwenden?

    2. Das sehe ich tatsächlich anders.

    VaporiZed schrieb:

    Die Funktion soll ja keine Bestellungen suchen. Sondern eine Liste von leeren Bestellungen zurückgeben.

    Die Funktion SOLL offene Kundenbestellungen zurückgeben.
    Also der Kunde Hans bestellt bei mir einen Sack Hundefutter von Firma XY. Wenn ich nun eine Bestellung bei der Firma XY auslöse, soll eine Form aufploppen, die mir anzeigt, Hans will einen Sack Hundefutter.
    Ah wobei. Retreive ist irgendwie doch besser als Search - ich glaube RetrieveOpenCustomerOrders - oder sowas wären besser als mein Name.

    3. Oh - stimmt. Das war ein Denkfehler von mir. Bisher dachte ich immer ich schiebe damit eine Zeile der Tabelle in die Variable.
    Aber es ist ja das ganze Ding - also umbenennen in CustomerOrder

    4. Das habe ich so vom @ErfinderDesRades übernommen und rumprobiert. An manchen Stellen (ich nutze diese Schleife auch für andere Datenabfragen) kommt es zum Fehler, wenn es keine Daten in der DataTable gibt.

    5.Uff, das Thema Tuple hatten wir doch vor kurzem erst. Ist natürlich viel schöner als meine List of String

    Und eine Frage noch von mir:
    Egal ob ich nun die Tuple an die neue Form übergebe, oder eine mit Linq erstellte Liste, es bleibt ja immer eine "Kopie" der Daten im DataTable.
    Also muss ich mir die ID der originalDatarow übergeben, und nach einem ändern die DataTable wieder nach der ID durchsuchen, um Änderungen im DataSet zu speichern.
    Schöner wäre es ja, wenn ich anstelle meines ungebunden Dgv, welches ich dann mit der Kpoie der Daten befülle, ein gebundenes Dgv erstelle, indem ich aus dem DataSet die im TableSupplier eingeschachtelte TableCustomerORder befülle.
    Dieses muss dann nach "offenen" Bestellungen gefiltert werden. Dann spare ich mir dieses ID hin und hergewurschtel.

    Am besten bastel ich mal eine Demo, oder versteht ihr was ich meine?

    DerSmurf schrieb:

    Aber Exit Function ist dann ungenau/unsauber? Immer mit Return verlassen?
    Ja und ja. Wenn Du das nur als Warnung lässt und mit Exit Function arbeitest, wird (anscheinend) Nothing zurückgegeben. Und das wird dann in den Return-Typ uminterpretiert. Somit gibt ne Function mit Rückgabe List(Of String) Nothing zurück, während eine Boolean-Function False zurückgibt. Daher immer besser was konkretes zurückgeben.

    DerSmurf schrieb:

    Egal ob ich nun die Tuple an die neue Form übergebe, oder eine mit Linq erstellte Liste, es bleibt ja immer eine "Kopie" der Daten im DataTable.
    Es ist nicht egal. Wenn Du Tuples verwendest, so wie ich es tat, dann kopierst Du die Werte und gibst somit nur die Werte zurück. Wenn Du eine mit LINQ erstellte Liste zurückgibst, gibst Du eine CustomerOrderRow-List zurück. Und Änderungen an diesen Listeneinträgen führen zu Änderungen in der DataTable. Unterschied Wertetyp - Referenztyp. Deine Rows sind Klasseninstanzen, somit Referenztyp, somit Änderungen an "Kopien" = Änderungen am Original.

    DerSmurf schrieb:

    versteht ihr was ich meine?
    Ehh, nein, momentan leider nicht.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    Na mir geht es darum, direkt mit den Daten aus dem DataTable zu arbeiten und eben nicht mit einer Kopie.
    Denn wenn ich in der Kopie etwas verändere, muss ich ja den entsprechenden Eintrag im Original erst suchen (ID) und die Werte auch im Original - laso im DataTable ändern.
    Aber dann habe ich den MSDN Artikel über Linq falsch verstanden.
    Den ich habe es so verstanden, dass ich eine gefilterte Kopie der Daten erstelle - aber dann schaue ich mir das heute / morgen erstmal genauer an.
    Denn das scheint ja genau das zu sein was ich brauche.

    Demoprojekt schaffe ich heute über Tag nicht - lade ich heute Abend oder heute Nacht hoch.
    Dann sehe ich aber den Sinn der List(Of String)-Rückgabe aus Post#1 nicht. Dann gib doch gleich die ganze Row(-List) zurück.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    Ja sowas in der Art meinte ich mit "arbeiten mit den Originaldaten".
    Aber mir fällt kein Weg ein, dass hinzubekommen.
    Habe nun mal eine Demo angehängt. (bitte nicht auf Design achten)

    Wenn ich eine Bestellung von Firma1 tätige, soll mir die Kundenbestellung "Name1" angezeigt werden, weil die Spalte Ordered leer ist. Die Bestellung von "Name" soll hingegen nicht angezeigt werden, weil Spalte Ordered nicht leer ist.
    Gäbe es nun noch mehr Kundenbestellungen mit leerem Ordered Eintrag, sollen diese ebenfalls angezeigt werden.

    Achja Edit:
    Deine Änderungsvorschläge habe ich in diesem Upload noch nicht eingepflegt, weil ich ohnehin denke, dass einfach mein gesamter Ansatz - und damit auch mein Code - mist sind.
    Allerdings habe ich aus allen anderen Funktionen in meinem Programm das Exit Function verbannt.
    Dateien
    • CustOrders.zip

      (50,63 kB, 9 mal heruntergeladen, zuletzt: )

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

    Was mir bei deinem Code auffällt ist, dass du keine Form instanziierst...

    VB.NET-Quellcode

    1. Dim frm As New frmCustomerOrders
    2. frm.CustomerOrderBindingSource.DataSource = Me.DataSet1
    3. frm.CustomerOrderBindingSource.Filter = String.Format("Ordered Is Null And SupplierID = {0}", SelectedSupplier.ID)
    4. frm.Show()
    Zu deinem Anzeige- und Bearbeitungsproblem:
    Openorders = SearchCustomerOrders(SelectedSupplier.ID)
    Du könntest die BS von CustomerOrder nach row.IsOrderedNull AndAlso row.SupplierID = SupplierID
    in der Form frmCustomerOrders filtern und dann hast deine Originaldaten zum Bearbeiten in der DGV gefiltert angezeigt...
    (den Filter für die BS mit SupplierID kannst sogar in der Haupt-Form setzen - siehe Code oben...)

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

    hier mal ein Beiispiel mit Linq (ob das nu in die akt. Diskussion passt oder nicht):

    VB.NET-Quellcode

    1. Private Function SearchCustomerOrders(SupplierID As Integer) As List(Of CustomerOrderRow)
    2. Return (From row In DtsSettings.CustomerOrder Where row.IsOrderedNull AndAlso row.SupplierID = SupplierID Select row).Tolist
    3. End Function
    Und das wiederum ist eiglich so simpel, dass man infrage stellen kann, ob man dafür überhaupt eine Funktion implementieren sollte.

    Es lohnt sich, sich mit Linq zu beschäftigen.
    Als Erläuterung zu Post#7:

    VB.NET-Quellcode

    1. Private Function SearchCustomerOrders(SupplierID As Integer) As List(Of DataSet1.CustomerOrderRow)
    2. Return DataSet1.CustomerOrder.Where(Function(x) x.IsOrderedNull AndAlso x.SupplierID = SupplierID).ToList
    3. End Function

    Und dann in Button1_Click statt mit List(Of String) eben mit List(Of DataSet1.CustomerOrderRow) arbeiten und an das Subform weiterreichen. Allerdings wird dann natürlich schon rumgemeckert, dass Ordered DBNull ist. Da musst Du dann selber Dir was überlegen. Allerdings: Was soll Ordered darstellen? Ist ja schließlich ein String.

    Oh, da hab ich mir wohl mit meiner Antwort etwas zuviel Zeit gelassen ...
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    Hallo ihr drei.
    Vielen Dank für eure Vorschläge / Erklärungen!

    VB1963 schrieb:

    Was mir bei deinem Code auffällt ist, dass du keine Form instanziierst...

    Ja, das ist ein Fehler, der mir eigentlich nicht mehr passiert. Allerdings neige ich dazu etwas schlampig zu werden, wenn ich "nur mal schnell" etwas testen möchte.

    VaporiZed schrieb:

    Allerdings: Was soll Ordered darstellen? Ist ja schließlich ein String.

    Sorry! Die Spalten OrderDate und Ordered sollten eigentlich als DateTime formatiert sein.

    Nun habe ich aber noch eine Frage zu diesen beiden Vorgehensweisen, denn letzlich machen ja beide das gleiche.
    Bei beiden möglichkeiten habe ich im DGV der frmCustomerOrders nur die offenen Kundenbestellungen (SupplierID = ID und ..IsOrderedNull).
    Eine Bearbeitung die ich nun vornehme wird ja direkt ins DataSet geschrieben.

    Möglichkeit 1 wäre die von @VB1963. Ich filtere die Bindingsource (in der frmCustomerOrders - damit ich in der Hauptform den Filter nicht zurücksetzen muss).
    Möglichkeit 2 von (@'ErfinderDesRades' und @VaporiZed). Mittels Linq eine Klasseninstanz (stimmt das) meiner DataTable herstellen, damit Änderungen an dieser Instanz = Änderungen am Original.
    Die DGV in frmCustomerOrders binde ich dann an diese List(Of DataSet1.CustomOrderRow)

    Welche dieser beiden herangehensweisen ist denn empfehlenswerter?

    DerSmurf schrieb:

    Mittels Linq eine Klasseninstanz (stimmt das) meiner DataTable herstellen
    Inhaltlich: Nein. Die Klasse ist der ganze DataTable-Code in Deiner tDS-Datei. Die Klasseninstanz ist das »lebende« Objekt, in das Du Daten reinstopfen kannst. Also hast Du zur Laufzeit bereits eine Klasseninstanz Deiner DataTable. Was Du damit machst: Du nimmst einige Zeilen daraus und packst sie in eine Liste. Das ist ... tja. Ein eigenes Objekt erstellen. Vom Typ List(Of CustomerOrderRow). Nicht mehr und nicht weniger.

    DerSmurf schrieb:

    Welche dieser beiden herangehensweisen ist denn empfehlenswerter?
    Such's Dir aus. Ich selber arbeite selten mit Filtern. Die ganze Expressionsyntax sind mir zu VB6.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Häufig von mir verwendete Abkürzungen: CEs = control elements (Labels, Buttons, DGVs, ...) und tDS (typisiertes DataSet)
    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht in den Spekulatiusmodus gehen.
    OK :)
    Dann versuche ich mal das von @VB1963 umzusetzen.
    Ich glaube mit der "Filtergeschichte" könnte ich mir einige DBNull Zickereien ersparen.
    Ich "säubere" mal meinen Code und dann präsentiere ich in den nächsten Tagen das Ergebnis.
    Welche Methode besser ist - sagen wir einmal: Welche Methode moderner ist - und dass ist natürlich LINQ...
    Abhilfe gegen die DBNull-Zickereien habe ich dir schon einmal gepostet...
    DataSet only - Datarow kopieren, mit Form und möglichen Useränderungen

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

    Hallo Leute
    Sorry für meine echt späte Reaktion, allerdings habe ich diese Woche keine Zeit gefunden an meinem Programm weiter zu wurschteln.

    Letztlich habe ich mich dann doch gegen die Filtermethode entschieden.
    Zum einen wegen des Rates von @VB1963, zum anderen, weil ich ja eh das DataSet durchforsten muss, um überhaupt festzustellen, ob es offene Kundenbestellungen gibt, ob also die Form überhaupt angezeigt wird.
    Für diesen Zweck ist ja der Code von @VaporiZed und @ErfinderDesRades
    Private Function SearchCustomerOrders(SupplierID As Integer) As List(Of DataSet1.CustomerOrderRow)
    Return DataSet1.CustomerOrder.Where(Function(x) x.IsOrderedNull AndAlso x.SupplierID = SupplierID).ToList
    End Function

    sehr viel schöner als meine Schleifenvariante. Und dann habe ich ja - neben der Prüfung, ob es zur Anzeige kommt, - eh schon eine gefilterte Liste. Jetzt das (komplette) DataSet mit einem Filter zu bearbeiten, ist ja irgendwie zwei mal das gleiche (Ergebnis), auf eine andere Art.
    Also übergebe ich einfach meine Liste an die neue Form und Binde das DGV entsprechend.
    Bitte die globale Variable übersehen.

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    2. Dim Openorders As New List(Of DataSet1.CustomerOrderRow)
    3. LoadSupplier()
    4. If SupplierBindingSource.Current Is Nothing Then Exit Sub
    5. Dim SelectedSupplier = DirectCast(DirectCast(SupplierBindingSource.Current, DataRowView).Row, DataSet1.SupplierRow)
    6. 'auf offene Kundenbestellungen prüfen
    7. Openorders = SearchCustomerOrders(SelectedSupplier.ID)
    8. If Openorders.Count > 0 Then
    9. Dim ShowOrders As New frmCustomerOrders
    10. With ShowOrders
    11. .OpenOrders = Openorders
    12. .Show()
    13. End With
    14. End If
    15. Dim order As DataSet1.OrderRow
    16. order = DataSet1.Order.NewOrderRow()
    17. order.OrderDate = Date.Today
    18. order.SupplierID = SelectedSupplier.ID
    19. DataSet1.Order.Rows.Add(order)
    20. End Sub

    VB.NET-Quellcode

    1. Private Function SearchCustomerOrders(SupplierID As Integer) As List(Of DataSet1.CustomerOrderRow)
    2. Return DataSet1.CustomerOrder.Where(Function(x) x.IsOrderedNull AndAlso x.SupplierID = SupplierID).ToList
    3. End Function

    VB.NET-Quellcode

    1. Public Class frmCustomerOrders
    2. Public OpenOrders As New List(Of DataSet1.CustomerOrderRow)
    3. Private Sub FrmCustomerOrders_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    4. Me.DataGridView1.DataSource = OpenOrders
    5. End Sub
    6. End Class[/spoiler][spoiler]


    Nun kommt es aber, wie von @VaporiZed prophezeit zur DBNull Exeption.

    Das Problem ist, dass der Lösungsvorschlag von @VB1963 hier nicht funktioniert.
    Die Spalten Ordered und Called sind ja beide vom Typ DateTime - also Strukturypen.
    Hier ist es ja nicht möglich einen leeren Wert zu hinterlegen.
    Da weiß ich gerade nicht so ganz wie ich weiter kommen soll.

    Edit: Und ich Frage mich, ob dann nicht doch die Filtermethode angenehmer ist.
    Denn hier ist es ja einfach wurscht, ob DNull oder nicht - und wenn das Filterkriterium nicht mehr erfüllt ist (ich also bei Ordered ein Datum angebe), verschwindet der Eintrag ja aus meinem DataGridview.
    Ich kann also einfach prüfen, ob es noch offene Bestellungen gibt (es dürfte sich ja auch BindingSource.Count nur auf die gefilterten Werte beziehen) - und die Form schließen.
    Dies müsste ich ja in der "ListenVariante" auch erst noch selbst erstellen.

    Sollte ich dann evtl. doch über eine Filter Variante nachdenken?

    Wem das nicht reicht hole ich hier etwas weiter aus, was da in meinem jetzigen Excel Programm passiert.
    Spoiler anzeigen
    ​Ich speichere Bestellungen - bzw. Rechnungsdaten - um Umsätze und dergleichen anzeigen zu können.
    Darüberhinaus speichere ich Kundenbestellungen.
    Mache ich nun eine neue Bestellung, wird in meiner Exceltabelle bei dieser Firma eine neue Zeile eingefügt, um (später) die Rechnungsdaten zu dieser Bestellung speichern zu können.
    Außerdem werden die Kundenbestellungen durchforstet, un offene Kundenbestellungen dieser Firma angezeigt.
    Wenn es eine Bestelldatei gibt wird diese nun geöffnet und die offenen Kundenbestellungen können mit einem Klick eingefügt werden, entweder als neue Position, oder sofern die Art.Nr. bereits vorhanden ist, an dieser Stelle.

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

    Bei Datumsangaben nehme für 'leere Werte' einfach Date.MinValue und lasse diesen Wert im CellFormatting-Event leer darstellen... (ist bei Datetypen gar nicht notwendig)
    Du behandelst im Code dann Date.MinValue als Leerwert und es kommt auch keine solch DBNull-Exception...

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

    Aber dann muss ich doch AllowDBNull auf False setzen und einen Default Wert festlegen.
    Und schwups habe ich doch da ein Datum drinne.
    Jetzt müsste ich doch bei jeder Anzeige dieses Tables aus dem DataSet per Code die Anzeige meines Standartvalues unterbinden.
    Oder hab ich das was falsch verstanden?

    Wäre es da dann nicht einfacher (weil die ganze DBNull Problematik entfällt) - doch eine etwas abgewandelte Version meiner Schleife in Verbindung mit der Filter Variante zu nehmen?

    VB.NET-Quellcode

    1. Private Function SearchCustomerOrders(SupplierID As Integer) As Boolean
    2. Dim COrderRow = DataSet1.CustomerOrder
    3. If COrderRow.Rows.Count < 1 Then Return False
    4. For Each row In COrderRow
    5. If row.IsOrderedNull AndAlso row.SupplierID = SupplierID Then
    6. Return True
    7. End If
    8. Next
    9. Return False
    10. End Function
    11. Private Sub Aufruf
    12. If SearchCustomerOrders(SelectedSupplier.ID) Then
    13. Dim frm As New frmCustomerOrders
    14. frm.CustomerOrderBindingSource.DataSource = Me.DataSet1
    15. frm.CustomerOrderBindingSource.Filter = String.Format("Ordered Is Null And SupplierID = {0}", SelectedSupplier.ID)
    16. frm.Show()
    17. End If
    18. end sub

    Diese Funktion gibt bei der ersten offenen Kundenbestellung True zurück, so wird nicht die gesamte DataTable durchforstet.
    Je nach True oder False, lasse ich dann meine Form mit gefilterter Bindingsource anzeigen.
    Das erspart mir folgendes:
    • keine DBNull Problematik
    • in der Form angezeigte Kundenbestellungen, welche ich über die Form bestelle, verschwinden automatisch

    Edit: Nur für mich so zum Verständnis. Ist nicht auch DBNull sinnvoller als irgendein "Dummy" Wert?
    Also so aus Speichersicht, etc.

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