Neuerstellen eines umfangreichen Projektes - aus schlechtem Code mach guten Code

  • VB.NET

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

    Das ist eine Möglichkeit. Du kannst auch die ProgressReporter im MainForm haben und dem per Export-Class-Event mitteilen, dass sich was getan hat. Der ProgressReporter ist ja nur dafür da, aus dem Nebenthread Daten an den Hauptthread zu vermitteln. Also:
    Variante A: Export-Class wechselt mit eigenen ProgressReporter die Threads, um die Integerdaten weiterzugeben und danach teilt man dies per Hauptthreadevent ans MainForm mit.
    Variante B: Export-Class teilt die Integerdaten per Event dem MainForm mit und das MainForm kümmert sich per ProgressReporter um den Threadwechsel.
    Wie Du willst.

    btw: ArticleAmount ;)
    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.

    VaporiZed schrieb:

    btw: ArticleAmount

    Jo. Schlecht Angewohnheit :o - Ich kümmer mich drum.

    0. Ich nehme an, ob ich ProgressReporter Variante A, oder B verwende ist wurscht. Ich kann es auch immer so machen, wie es nun hier ist.
    Ich nummerier meine Fragen mal wieder, machts immer übersichtlicher.
    1. Kann ich dem ProgressReporter auch irgendwie zwei Werte mitgeben? Also in meinem Fall hier wäre ja der prozentuale Fortschritt für die ProgressBar und der aktuelle Artikel (z.B. für Artikel x von 12048 wird exportiert - im Label) denkbar.
    Oder brauche ich für sowas dann ein neues Event? Ich könnte ja an der "ProgressBerechnungsStelle" ein Event feuern, welches den Index des aktuellen Artikels mitbekommt.
    Und muss ich mir da über die Performance gedanken machen? Im jetzigen Stand meiner Anwendung müsste ja, das ProgressReporter.ProgressChanged Event jedes mal Feuern, wenn der nächste Artikel verarbeitet wird.
    Das wären dann bei meinem Datensatz hier 12048 mal. Oder ist der Mehrwert für den User (er sieht ja über die ProgressBar, dass noch was passiert) eigentlich immer größer, als der Performancegewinn ohne Progress Report?
    2. Könntest du mal einen Blick auf meine csv Sub werfen? Ich vermute hier einen groben Schnitzer, denn der funktionierende Export dauert Ewigkeiten.
    Die Funktion GetCellValues gibt ein Object zurück, da ich diese auch im Exceleport verwende. Wenn ich hier einen String zurückgebe, würden alle meine Zahlen in Excel Strings sein.
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Sub StartDGVtoCSVExport(ExportInfos As (ColumnsToExport As List(Of DataColumn), Exportcsv As Boolean, Delimiter As String), Filepath As String)
    2. Dim ColumnsToExport As List(Of DataColumn) = ExportInfos.ColumnsToExport
    3. Dim Delimiter As String = ExportInfos.Delimiter
    4. Dim csvFile As String = ""
    5. For i = 0 To ColumnsToExport.Count - 1
    6. Dim Colname = ColumnsToExport(i).ColumnName
    7. If Colname.Contains("ID") Then Colname = Colname.Replace("ID", "")
    8. csvFile &= Colname & Delimiter
    9. Next
    10. csvFile &= Environment.NewLine
    11. Dim ArticleAmount = _BindingSource.Count - 1
    12. For i = 0 To ArticleAmount
    13. For Each col In ColumnsToExport
    14. csvFile &= GetCellValue(i, col, Delimiter).ToString & Delimiter
    15. Next
    16. csvFile &= Environment.NewLine
    17. AbstractProgressReporter.Report(CInt((i + 1) * 100 / ArticleAmount))
    18. Next
    19. My.Computer.FileSystem.WriteAllText(Filepath, csvFile, False)
    20. End Sub
    21. Private Function GetCellValue(i As Integer, col As DataColumn, Delimiter As String) As Object
    22. Dim DataRow As DataRowView
    23. Dim CellValue As Object
    24. DataRow = DirectCast(_BindingSource.Item(i), DataRowView)
    25. Select Case col.ColumnName
    26. Case = "KategorieID"
    27. If Not DataRow(col.ColumnName).ToString = "" Then
    28. CellValue = GetCategoryName(CInt(DataRow(col.ColumnName)))
    29. Else
    30. CellValue = ""
    31. End If
    32. Case = "WarengruppeID"
    33. If Not DataRow(col.ColumnName).ToString = "" Then
    34. CellValue = GetProductGroupName(CInt(DataRow(col.ColumnName)))
    35. Else
    36. CellValue = ""
    37. End If
    38. Case = "LieferantID"
    39. If Not DataRow(col.ColumnName).ToString = "" Then
    40. CellValue = GetSupplierName(CInt(DataRow(col.ColumnName)))
    41. Else
    42. CellValue = ""
    43. End If
    44. Case = "VertreterID"
    45. If Not DataRow(col.ColumnName).ToString = "" Then
    46. CellValue = GetRepresentativeName(CInt(DataRow(col.ColumnName)))
    47. Else
    48. CellValue = ""
    49. End If
    50. Case Else
    51. If Not DataRow(col.ColumnName).ToString Is Nothing Then
    52. CellValue = DataRow(col.ColumnName)
    53. Else
    54. CellValue = ""
    55. End If
    56. End Select
    57. Return CellValue
    58. End Function


    3. das abbrechen
    Um das zu realisieren habe ich nun auf meinem ToolStrip einen abbrechen Button. Nun muss ich in dessen KlickEvent meine neu angelegte Boolean Eigenschaft "Cancel" auf True setzen.
    Aber ich deklariere meine Exportklasse ja innerhalb der Aufrufenden Sub:

    VB.NET-Quellcode

    1. Private Sub ExportDGV()
    2. '[...]
    3. Dim ExportClass As New Export(DGVToExport)
    4. AddHandler ExportClass.ExportStartet, AddressOf ShowStatusStripForExport
    5. AddHandler ExportClass.ExportFinished, AddressOf HideStatusStripForExport
    6. AddHandler ExportClass.ProgressReporter.ProgressChanged, AddressOf ShowExportProgress
    7. ExportClass.CollectUserDataForDGVExport()
    8. End Sub

    Im Button Klick Event kann ich ja jetzt nur sowas wie: ​ExportClass.Cancel = True machen, wenn diese Exportklasse in meiner Hauptform modulweit gültig ist. (stimmt doch oder?)
    Also muss ich sie oben als Private deklarieren.
    Ich komme also nicht um sowas: ​ Private Test As New Export(Nothing) drumherum? Oder geht das auch anders?

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

    1. Der ProgressReporter nimmt beim Konstruktor ein generisches Argument. Statt Integer kannst Du also eine eigene Klasse oder auch ein (Integer, Integer)-Tupel reinhauen:

    VB.NET-Quellcode

    1. Private ReadOnly ProgressReporter As New Progress(Of (Integer, Integer))
    2. Private ReadOnly AbstractProgressReporter As IProgress(Of (Integer, Integer)) = ProgressReporter

    2. Bei mir geht die Methode mit den Testdaten sehr schnell. Wieviele Datensätze hast Du? Dann bastel ich nochmal ein paar dazu und teste es erneut. Kommentier mal testweise den ProgressReporter aus und danach das Schreiben in die Datei, um so langsam rauszufinden, was der Zeitfresser ist. btw: My.Computer.FileSystem.WriteAllText -> IO.File.WriteAllText
    3.

    VB.NET-Quellcode

    1. Private Test As Export 'Deklaration, keine Definition/Instanziierung

    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.
    Soho. Habe soweit alles umgesetzt. Läuft alles, auch abbrechen geht reibungslos. Zwei Probleme gibt es noch.

    VaporiZed schrieb:

    Bei mir geht die Methode mit den Testdaten sehr schnell. Wieviele Datensätze hast Du?

    Mein original Datensatz hat 12048 Einträge. Ich hau die Solution an, im Load Event werden 12048 Dummy Artikel (und andere benötigte DataColumns - Warengruppe, Kategorie, Lieferant) angelegt, wenn es keine Artikel im Dts gibt.
    Kannst also im Datenordner die Settings.xml löschen, wie du willst, Daten werden erzeugt (bin ja lernfähig :o)
    1. Problem:
    Wenn du nun mit dem Dummy Datensatz alle Spalten in den Artikeln in csv exportierst, brauchst du einen Kaffee.
    Der Weg zum export:
    Spoiler anzeigen
    - grüner Button (dritter von links) im Menu Strip
    - Button mit Pfeil nach rechts (9. von links) und Tabelle exportieren
    - auf den Button "alles auswählen", um alle Spalten zu exportieren und csv auswählen (oder eben xlsx für Problem Nr. 2)
    - Export starten, rest ist selbsterklärend

    Im Gegensatz hierzu geht der Export der Kassendaten rubbeldizubbel. Hier erfolgt der Export auch in eine csv, aber mit sehr viel weniger Spalten.

    2. Problem:
    Beim Export in xlsx wird mir auf der MainForm das StatusStrip nicht gescheit angezeigt. Erst wenn der Export fertig ist.
    Der abbrechen Button ist nicht sichtbar, und auch das Label nicht. Nur die ProgressBar läuft.
    Das Problem lässt sich nicht lösen, indem ich die Spalten mehrfach (also wirklich oft, dass der Export dauert) exportiere.
    Dateien
    • Theo.zip

      (9,19 MB, 29 mal heruntergeladen, zuletzt: )
    1. Das kommt davon, wenn man mit einem String arbeitet. Ein String wird nicht durch weitere Strings ergänzt. Es wird ein neuer String erschaffen. Daher:

    VB.NET-Quellcode

    1. Private Sub StartDGVtoCSVExport(ExportInfos As (ColumnsToExport As List(Of DataColumn), Exportcsv As Boolean, Delimiter As String), Filepath As String)
    2. Dim ColumnsToExport As List(Of DataColumn) = ExportInfos.ColumnsToExport
    3. Dim Delimiter As String = ExportInfos.Delimiter
    4. Dim csvFile As New Text.StringBuilder
    5. For i = 0 To ColumnsToExport.Count - 1
    6. Dim Colname = ColumnsToExport(i).ColumnName
    7. If Colname.Contains("ID") Then Colname = Colname.Replace("ID", "")
    8. csvFile.Append(Colname & Delimiter)
    9. Next
    10. csvFile.AppendLine()
    11. Dim ArticleAmount = _BindingSource.Count - 1
    12. For i = 0 To ArticleAmount
    13. If _Cancel Then Return
    14. For Each col In ColumnsToExport
    15. csvFile.Append(GetCellValue(i, col, Delimiter).ToString & Delimiter)
    16. Next
    17. csvFile.AppendLine()
    18. AbstractProgressReporter.Report(CInt((i + 1) * 100 / ArticleAmount))
    19. Next
    20. File.WriteAllText(Filepath, csvFile.ToString)
    21. End Sub

    Und die Sache ist (bei mir) nach ca. 100 ms um.

    2. FormatData geht relativ fix. FormatColumns dauert und ExcelPackage.SaveAs(fi) auch etwas. Da dort keine AbstractProgressReporter (möglich) sind, kannst Du da auch nix sehen. Wenn ich allerdings versuche, da was zu debuggen, crasht das Programm. Offensichtlich mag Excel keine nebenläufigen Bearbeitungen.
    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.
    Zu 2. Mir ist aufgefllen, dass ich StatusStrip1.Visible = False im Designer gesetzt habe. Das habe ich nun korrigiert und verstecke das Control (sowie dessen Cancel Button) im Form Load der Hauptform.
    Dadurch kann ich beim Excel Export zumindest den Fortschritt in der ProgressBar sehen. Auch wenn der Cancel Button erst etwas später sichtbar wird Da ich aber in der Praxis noch nie alle Spalten gleichzeitig exportiert habe, geht der Export so schnell, dass ein Abbruch eigentlich noch nie nötig war. Ich kann also mir dem Ist Zustand sehr gut leben.
    Edit: Ich habe das StatusStrip an den unteren Bildschirmrand verschoben. Nun siehts gescheit aus. Auch wenn es, wie du ja schon sagtest, scheint, als möge Excel keine Nebenläufigkeit (aber der Status läuft immerhin)

    Zu 1. Sag mal, was soll der Mist!?
    Ich bastel da einen schicken Button hin, um den Export abzubrechen und schreibe eine Sub, mit der es problemlos möglich ist ganz in Ruhe - nach einer Tasse Kaffee - auf diesen Button zu klicken.
    Dann kommst du daher, änderst drei Zeilen im Code und ... und es wird nahezu unmöglich auf diesen tollen neuen Button zu klicken. Ehe meine Maus da oben ist, ist auch schon alles fertig ...
    Danke! :o) Ich habe

    VB.NET-Quellcode

    1. Dim csvFile As New Text.StringBuilder
    auch auf meine Sub WriteCSVForCashRegister und freue mich schon, wenn das neue Programm in Betrieb geht.
    Denn auch hier gehts jetzt natürlich in einem Bruchteil der Zeit - und diese Funktion nutze ich wirklich häufig!

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

    Soho Ich habe nun meine Rechnungskontrolle fertig.
    Der Plan hier ist, dass ich eine (pdf) Rechnung mittels OCR (Abbyy Fine Reader) in xls umwandel. Diese lade ich in mein Programm und stelle sie in einem DGV dar.
    Nun wähle ich den Lieferanten aus, sowie die Spalten, in denen Infos zu finden sind. (Also Art.Nr. in Spalte 1 | Artikelname in Spalte 2, usw.).
    Einzige Ausnahme macht hier das Feld für Rabatt (falls du dich wunderst warums im DataSet ein String ist). Hier kann die Spalte vergeben werden (also z.B. 6), oder auch ein fester Rabatt (=30).
    Nach einem Klick auf Kontrolle, werden die Daten aus dem DGV ins DataSet geschrieben, und mit den gespeicherten Artikeln (anhand der Artikelnummer) verglichen.
    Es wird nun gespeichert, ob ein Artikel korrekt berechnet ist, zu teuer, zu günstig, oder ob er nicht gefunden wird.
    Das ganze wird in einem weiteren DGV angezeigt (welches hier im UploadProgramm nicht der endgültigen Version entspricht - da verschiebe ich es auf eine UC, welche unterhalb der Artikel angezeigt wird).

    Der Code ist vollständig fertig und funktioniert. Ich habe aber das Gefühl, dass hier einiges an Verbesserung möglich sein könnte. (Insbesonders in der Sub SaveArticleInDts)
    Es wäre ein Traum, wenn du hier mal rüber schauen könntest (nur diese eine Sub würde mir vollkommen reichen).
    Sie befindet sich auf der UCRechnungskontrolle. Im GUI hat die Rechnungskontrolle noch kein Bildchen, dann brauchst du da nicht suchen :o).
    Eine csv Datei liegt dem Upload bei - diesen bekommst du hinein mit einen Klick auf den orangenen Plus Button (den ersten) auf der UCRechnungskontrolle.
    Dateien
    • Theo.zip

      (10,2 MB, 22 mal heruntergeladen, zuletzt: )
    Ach, Du und Article:rolleyes:
    Da wären als Möglichkeit
    If-Umkehrungen

    VB.NET-Quellcode

    1. For
    2. If Foo Then
    3. If Bar Then
    4. 'Restcode
    5. End If
    6. End If
    7. Next
    -->

    VB.NET-Quellcode

    1. For
    2. If Not Foo OrElse Not Bar Then Continue For
    3. 'Restcode
    4. Next


    Aber wenn Du es dabei belassen willst, ist es Deine Sache.

    Der Teil

    VB.NET-Quellcode

    1. For Each SupplierSpecificArticle In DirectCast(DirectCast(BSLieferant.Current, DataRowView).Row, DtsDaten.LieferantRow).GetArtikelRows
    2. If NewItem.ArtNr = SupplierSpecificArticle.ArtNr Then
    ist mit LINQ etwas einfacher:

    VB.NET-Quellcode

    1. Dim FoundItem = DirectCast(DirectCast(BSLieferant.Current, DataRowView).Row, DtsDaten.LieferantRow).GetArtikelRows.SingleOrDefault(Function(x) x.ArtNr = NewItem.ArtNr)
    2. If FoundItem.NotNull Then
    3. NewItem.GespeicherterPreis = FoundItem.Einkaufspreis
    So ziemlich immer, wenn Du eine lokale gefunden-Variable einbaust, kannst Du davon ausgehen, dass eine Function oder LINQ an der Stelle besser wären.
    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.

    VaporiZed schrieb:

    Ach, Du und Article …

    Naja, immerhin ist ein Fortschritt zu sehen. Ich verwende zwar den Subnamen SaveArticleInDts und die Variable ArticleCounter, aber immerhin schonmal NewItem.
    Glaube wenn ich mit dem Programm durch bin, muss ich nochmal alles nach Article durchsuchen und einen haufen Stellen ersetzen...

    So, habe ein bisschen gebraucht, die Änderungen umzusetzen. Ich weiß nicht genau warum, aber die doppelte If Umkehr war ein bisschen Brainfuck Glaube ich habe es an zu vielen Stellen versucht und da hats ein bisschen gedauert (und ein paar Anläufe gebraucht), bis ich erkannt habe, dass es einfach nicht geht.
    Hier der Code:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Private Sub SaveItemInDts()
    2. Dim ItemCounter = 0
    3. Dim SelectedSupplier = BSLieferant.At(Of DtsDaten.LieferantRow)
    4. Dim ColumnArtNr = SelectedSupplier.SpalteArtikelNummer - 1
    5. Dim ColumnName = SelectedSupplier.SpalteArtikel - 1
    6. Dim ColumnPrice = SelectedSupplier.SpaltePreis - 1
    7. Dim Calculate = SelectedSupplier.Preisausrechnen
    8. Dim DiscountText = SelectedSupplier.SpalteRabatt
    9. Dim ColumnDiscount As Integer
    10. Dim Discount As Double
    11. Dim FixDiscount = False
    12. If SelectedSupplier.Preisausrechnen Then
    13. If Integer.TryParse(DiscountText, ColumnDiscount) Then
    14. ColumnDiscount -= 1
    15. Else
    16. If DiscountText.Substring(0, 1) = "=" Then
    17. Double.TryParse(DiscountText.Substring(1, DiscountText.Length - 1), Discount)
    18. FixDiscount = True
    19. End If
    20. End If
    21. End If
    22. Dim _Dts = DirectCast(BSMain.DataSource, DtsDaten)
    23. _Dts.Rechnungskontrolle.Clear()
    24. Dim NewItem As DtsDaten.RechnungskontrolleRow
    25. For i = 0 To DGVRechnungskontrolle.Rows.Count - 1
    26. If DGVRechnungskontrolle.Rows(i).Cells(ColumnArtNr).Value Is Nothing OrElse DGVRechnungskontrolle.Rows(i).Cells(ColumnPrice).Value Is Nothing Then Continue For
    27. Dim Price As Double
    28. If Double.TryParse(DGVRechnungskontrolle.Rows(i).Cells(ColumnPrice).Value.ToString, Price) Then
    29. NewItem = _Dts.Rechnungskontrolle.NewRechnungskontrolleRow()
    30. ItemCounter += 1
    31. NewItem.Artikelzaehler = ItemCounter
    32. NewItem.ArtNr = DGVRechnungskontrolle.Rows(i).Cells(ColumnArtNr).Value.ToString
    33. If DGVRechnungskontrolle.Rows(i).Cells(ColumnName).Value IsNot Nothing Then
    34. NewItem.Name = DGVRechnungskontrolle.Rows(i).Cells(ColumnName).Value.ToString
    35. Else
    36. NewItem.Name = "kein Name"
    37. End If
    38. If Calculate Then
    39. NewItem.Listenpreis = Price
    40. If Not FixDiscount Then
    41. If DGVRechnungskontrolle.Rows(i).Cells(ColumnDiscount).Value IsNot Nothing Then
    42. If Not Double.TryParse(DGVRechnungskontrolle.Rows(i).Cells(ColumnDiscount).Value.ToString, Discount) Then
    43. Discount = 0
    44. End If
    45. Else
    46. Discount = 0
    47. End If
    48. End If
    49. NewItem.Rabatt = Discount
    50. Price = Price * ((100 - Discount) / 100)
    51. Price = Double.Parse(Price.ToString("N2"))
    52. NewItem.Rechnungspreis = Price
    53. Else
    54. NewItem.Rechnungspreis = Price
    55. End If
    56. Dim PriceDifference As Double
    57. Dim FoundItem = DirectCast(DirectCast(BSLieferant.Current, DataRowView).Row, DtsDaten.LieferantRow).GetArtikelRows.SingleOrDefault(Function(x) x.ArtNr = NewItem.ArtNr)
    58. If FoundItem.NotNull Then
    59. NewItem.GespeicherterPreis = FoundItem.Einkaufspreis
    60. PriceDifference = Price - NewItem.GespeicherterPreis
    61. NewItem.Differenz = PriceDifference
    62. If PriceDifference = 0 Then
    63. NewItem.Ergebnis = "korrekt"
    64. ElseIf PriceDifference > 0 Then
    65. NewItem.Ergebnis = "zu teuer"
    66. Else
    67. NewItem.Ergebnis = "zu preiswert"
    68. End If
    69. Else
    70. NewItem.Ergebnis = "nicht gefunden"
    71. End If
    72. _Dts.Rechnungskontrolle.Rows.Add(NewItem)
    73. End If
    74. Next
    75. End Sub


    Und dann habe ich noch zwei Sachen.
    1. Ich habe eine Sub, die nach fehlenden Rechnungen sucht. Die Sub sucht also zu allen - im Dts gespeicherten - Rechnungen, ob es hierzu eine Datei gibt. Für alle fehlenden Rechnungen, wird dann in einer Form die Firma, die Rechnungsnummer und das Rechnungsdatum angegeben. (Ist aber eigentlich auch wurscht, was die Sub macht)

    VB.NET-Quellcode

    1. Private Async Sub StartMissingInvoiceSearch()
    2. RaiseEvent InvoiceSearchStartet("Fehlende Rechnungen werden gesucht")
    3. Dim Missinginvoices As New List(Of String)
    4. Missinginvoices = Await Task.Run(Function() Findmissinginvoices())
    5. If Missinginvoices.Count > 0 Then
    6. RaiseEvent InvoiceSearchFinished("")
    7. ShowMissingInvoices(Missinginvoices)
    8. Else
    9. RaiseEvent InvoiceSearchFinished("keine fehlenden Rechnungen gefunden")
    10. End If
    11. End Sub

    Hier habe ich versucht die Ausgabe der Funktion FindMissingInvoices in eine Tuple, bzw. eine List of Tuple zu ändern. Hierzu eine (funktionierende) Demosub:

    VB.NET-Quellcode

    1. Private Sub TupleTest()
    2. Dim TupleList = ReturnTuple()
    3. For Each TupleListItem In TupleList
    4. MessageBox.Show(TupleListItem.Item1 & " " & TupleListItem.Item2)
    5. Next
    6. End Sub
    7. Private Function ReturnTuple() As List(Of Tuple(Of String, Date))
    8. Dim TupleList As New List(Of Tuple(Of String, Date))
    9. For i = 1 To 5
    10. TupleList.Add(New Tuple(Of String, Date)("item" & i, System.DateTime.Now))
    11. Next
    12. Return TupleList
    13. End Function

    Nun spreche ich die Tuple Items ja mit TupleItem.Item1 usw. an. Das macht den Code ja dann nicht leserlicher, als meine List Of String aufzusplitten. Ich habe es nicht hinbekommen für die Tuple Items einen Namen zu vergeben.
    Also so in etwa:

    VB.NET-Quellcode

    1. Private Function ReturnTuple() As List(Of Tuple(Of Name as String, Datum as Date))
    2. messagebox.show(TupleItem.Name)

    bekomm ich das irgendwie hin?

    2. Ich mache mir Gedanken über das "EinstellbarMachenDesMwstSatzes". Und hätte gerne deine Meinung zu meiner Überlegung.
    Ich nutze die Mwst. bei Artikeln. Hier sind sie Teil der Kalkulation (EK + Mwst. + Marge = VK) und beim Eintragen einer Rechnung. Hier gebe ich aktuell den Netto19 und den Netto7 Betrag ein, damit das Brutto ausgerechnet werden kann.
    Die beiden Mehrwertsteuersätze würde ich im Dts in meiner Table "Einstellungen" speichern. Eine Spalte für volle Mwst. und eine Spalte für ermäßigte Mwst.
    Beim Eintragen einer Rechnung würde ich dann enstprechend die Spaltenüberschriften meines DGVs (wo die Rechnungen angezeigt werden) anpassen. Also Spaltenüberschrift Netto 19% und Netto 7%.
    Außerdem hole ich mir beim speichern einer neuen Rechnung, einfach die Steuersätze aus dem Dts (statt wie bisher die fest im Code hinterlegten Werte 19 und 7).
    Bei den Artikeln würde ich in einer ComboBox die Werte aus dem Dts anbieten, statt wie bisher die fest hinterlegten Werte 19 und 7.
    Wenn der User in den Einstellungen nun die Mwst ändert läuft eine Sub, Die abfragt, ob bei den gespeicherten Artikeln (wo ja dann der Mwst. Satz geändert werden muss), die Marge, oder der VK angepasst werden soll und das dann entsprechend tut.
    Also die Mwst für alle Artikel anpasst und dann entweder den VK gleich lässt (also die Marge anpasst), oder den VK anpasst (also die Marge gleich lässt).
    0.

    DerSmurf schrieb:

    bis ich erkannt habe, dass es einfach nicht geht.
    Eine einfach If-Umkehr ist immer dann möglich, wenn es keinen verzichtbaren Else-Teil gibt:
    Zeile#31: »Wenn der Ausdruck in eine Double umgewandelt werden kann, dann mach (hierganzvielCode), sonst … mach nix.« -> If-Umkehr.

    1. Mach es ohne das Wort Tuple:

    VB.NET-Quellcode

    1. Private Function ReturnTuple() As List(Of (Name As String, Datum As Date))
    2. '…
    3. Messagebox.Show(TupleItem.Name)

    2. ist nachvollziehbar, mach es so
    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.
    @DerSmurf Sollten sich neue Fragestellungen ergeben, die mit Post #1 nichts mehr zu tun haben, so möchte ich dich bitten, dafür jeweils einen eigenen Thread aufzumachen. Denn wenn zig Fragen in einem einzigen Thread behandelt werden, kann die Nachwelt damit nichts anfangen. Und ein Forum ist schließlich auch für die Nachwelt da. Es ist nämlich schwer vorstellbar, dass ein Problem 11 Thread-Seiten beansprucht. Aktuelle Themen aus dem Thread dürfen natürlich noch zuende behandelt werden. Danke.
    Besucht auch mein anderes Forum:
    Das Amateurfilm-Forum
    @Marcus Gräfe Ich habe alles in einen Thread verpackt, weil ich nicht das Forum mit Überlegungen zu meinem Programm vollbaren wollte.
    Aber du hast natürlich vollkommen Recht. Bei einem Großteil der Posts hier geht es um Themen und Probleme die problemlos auf andere Projekte adaptierbar sind.
    So wie sie hier allerdings versteckt sind, wird sie niemand finden.
    Also an dieser Stelle ein großes Dankeschön an alle Helfer, insbesondere an @VaporiZed.
    Da ich ein Problem habe, welches eher Anwendungsspezifisch ist, belebe ich diesen Thread (nur für dieses eine Problem) wieder.
    Das Problem betrifft die UCReklamation, hier wird eine falsche Reklamation geöffnet.
    Der Fehler lässt sich nachstellen, wenn du nach dem Starten der Anwendung auf der Startseite unten rechts bei "2 offene Reklamationen" auf das Label Firma2 klickst.
    Du kommst zu den Reklamationen (UCReklamationen) und die ClearableComboBox ist auf Firma2 gestellt.
    Das nun angezeigte DGV wird scheinbar korrekt gefiltert, wenn du aber die Reklamation öffnest (durch doppelklick auf die DGV Zeile) wird die Rekla von Lieferant1 angezeigt.
    Wenn du das Fenster nun schließt, springt die CCB auf Lieferant1.
    Klickst du auf der Startseite auf Lieferant1, passiert der Fehler nicht. Was läuft hier falsch?

    Außerdem habe ich auf der Form dlgReklamationbearbeiten das Problem, dass mir in der ComboBox CBReklamationsgrund kein Wert angezeigt wird. (Das ist die Form, die dir nach dem Doppelklick ins DGV angezeigt wird). Es ist aber möglich, den Reklamationsgrund zu ändern.
    Beim Anzeigen der Reklamation steht da aber nichts. Was habe ich hier falsch eingestellt?
    Edit: Es gibt an dieser Stelle bewusst keine Abhängigkeit im Dts zwischen Reklamation und Reklamationsgrund.

    *EXE-Anhang entfernt*

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Marcus Gräfe“ ()