Neuerstellen eines umfangreichen Projektes - aus schlechtem Code mach guten Code

  • VB.NET

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

    Neu

    bzgl. des Posts des Neuen: Habe ich gelöscht, weil das einfach nur ein kopierter Satz von mir aus Post#158 war. Spam-Verdacht, da sonst kein Zusammenhang zu sehen war ist.
    Ich kann was basteln. Aber letztenendes ist es Filterung des tDS mittels LINQ, nicht mit der FilterStringProperty der BS.
    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.

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

    Neu

    VaporiZed schrieb:

    Ich kann was basteln. Aber letztenendes ist es Filterung des tDS mittels LINQ, nicht mit der FilterStringProperty der BS.

    Nein da brauchst du nichts basteln, das reicht mir so. Ich denke aber das werde ich an dieser Stelle (noch) nicht machen, da meine Suche ja prinzipiell funktioniert und ich glaube auch nicht so schlecht gelungen ist.
    Ist es nicht so, dass Linq die modernere und wahrscheinlich deutlich flexiblere (und damit für meine Aufgabe hier, die besser geeignete Variante) ist, aber meine Variante auch ihren Zweck (gar nicht so schlecht) erfüllt?

    So. Alles andere habe ich umgesetzt. War jetzt irgendwie leichter als gedacht.
    1. Das Lieferantenproblem habe ich nun so gelöst:

    VB.NET-Quellcode

    1. Private Sub SetSupplier(RowIndex As Integer, ColumnTextBox As TextBox)
    2. Dim ColumnIndex As Integer
    3. Dim SupplierName As String
    4. If Integer.TryParse(ColumnTextBox.Text, ColumnIndex) AndAlso ColumnIndex > 0 Then
    5. If ColumnIndex - 1 <= DGVArtikel.ColumnCount Then
    6. If DGVArtikel.Rows(RowIndex).Cells(ColumnIndex).Value IsNot Nothing Then
    7. SupplierName = DGVArtikel.Rows(RowIndex).Cells(ColumnIndex).Value.ToString
    8. End If
    9. End If
    10. End If
    11. If SupplierName.IsEmpty Then
    12. CCBLieferant.Clear()
    13. Return
    14. Else
    15. CCBLieferant.UndoClear()
    16. End If
    17. Dim dts = DirectCast(BSMain.DataSource, DtsDaten)
    18. Dim SelectedSupplier = dts.Lieferant.FindSupplierByName(SupplierName)
    19. For i = 0 To BSLieferant.Count - 1
    20. If DirectCast(BSLieferant.Item(i), DataRowView).Row Is SelectedSupplier Then BSLieferant.Position = i
    21. Next
    22. End Sub

    Das UndoClear ist nötig, weil ich beim laden der Form ein CCBLieferant.Clear ausführe, damit nichts in der CCBLieferant angezeigt wird, wenn kein Artikel geladen ist (sonst steht ja der erste Lieferant drinne)

    Ich habe zusätzlich beim speichern eines Artikels dann noch eine Prüfung eingebaut:
    If CCBLieferant.IsEmpty Then Return (False, "Lieferant darf nicht leer sein")
    Diese ist beim bearbeiten eines Artikels aber immer True. Ich glaube, weil im Designer die IsEmpty Eigenschaft auf True steht. Aber ich kann das nicht ändern.
    Muss ich hier im Form Load ein CCBLieferant.UndoClear ausführen, oder geht das auch im Designer?

    2. Das ändern / Neuanlegen im Dts (die Lieferanten bearbeiten Geschichte) z.B. im dlgLieferantBearbeiten
    Habe ich umgesetzt, wie von dir vorgeschlagen. Funktioniert natürlich.

    3. Zum Datum:
    Ich übergebe nun z.B. DTPEKend.Value.Date (und die anderen drei Daten genauso).
    In der Suchenklasse siehts nun so aus:

    VB.NET-Quellcode

    1. Public Sub New
    2. '...
    3. If _SearchRetailPriceChanged Then
    4. _RetailPriceChangedStartDate = RetailPriceChangedStartDate.ToString("MM/dd/yyyy HH:mm:ss").Replace(".", "/")
    5. _RetailPriceChangedEndDate = RetailPriceChangedEndDate.ToString("MM/dd/yyyy HH:mm:ss").Replace(".", "/")
    6. End If
    7. ' und beim Verkaufspreis genauso
    8. End Sub
    9. Public Function BuildSearchstring() As String
    10. '...
    11. If _SearchRetailPriceChanged Then
    12. If Searchstring = "" Then
    13. Searchstring = $"DatumletzteKalkulationVK > #{_RetailPriceChangedStartDate}# AND DatumletzteKalkulationVK < #{_RetailPriceChangedEndDate}#"
    14. Else
    15. Searchstring = "(" & Searchstring & $") AND DatumletzteKalkulationVK > #{_RetailPriceChangedStartDate}# AND DatumletzteKalkulationVK < #{_RetailPriceChangedEndDate}#"
    16. End If
    17. End If
    18. return searchstring
    19. End Function

    Aber ich glaube (wenn ich hier nicht wieder einen Denkfehler habe), dass ich das "=" trotzdem brauche.
    Denn wenn ich am 05.08.2022 den VK eines Artikels geändert habe, dann stelle ich bei Startdate 05.08.2022 und bei EndDate auch 05.08.2022 ein.
    Ohne "=" würde nichts gefunden werden - ich müsste vom 04.08 bis 06.08. suchen.

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

    Neu

    zu 1.: mal schauen …
    zu 2.:

    DerSmurf schrieb:

    Funktioniert natürlich.
    danke, aber das ist nicht immer erwartbar. Selbst ich gehe schon lange davon aus, dass das, was ich mache, nicht funktioniert. Aus Erfahrung :rolleyes:
    zu 3.: Mach, wie es klappt. Die Details Deines Programms kenne ich nicht. Wenn das = gebraucht wird, nutze es.
    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.

    Neu

    Jo. Dann teste ich morgen Mal mit dem =.
    Ich muss ja aber eh noch schauen, ob DatumLetzteKalkulationVK, also das Datum im DTS eine Uhrzeit hat. (Das hatte ich vergessen)
    Falls ja, muss ich ja hier auch "um Mitternacht" speichern.
    Dann könnte es auch ohne Gleichheitszeichen gehen. Vielleicht. Mal gucken. Ich war so froh, dass der doofe Datumsvergleich funktioniert hat xD

    Heißt dein "Mal schauen", dass du später nochmal rüber schaust?

    Neu

    jou. Heute nicht mehr. Es wird Feierabendzeit für mich …
    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.

    Neu

    DerSmurf schrieb:

    Ist es nicht so, dass Linq die modernere und wahrscheinlich deutlich flexiblere (und damit für meine Aufgabe hier, die besser geeignete Variante) ist, aber meine Variante auch ihren Zweck (gar nicht so schlecht) erfüllt?
    Kann man so nix zu sagen.
    "Min Linq" kann auf zig verschiedene Weisen umgesetzt werden.
    Es gibt auch LinqToDataset, speziell für Dataset - dazu habich auch ein Tut gemacht.
    Das kann paar Dinge, die BindingSource.Filter nicht kann. Viele andere Dinge, die man eiglich erwarten würde, kanns aber auch nicht.
    Und wenn man mit LinqToDataset filtert, und will dann mit SpaltenHeader-Klick sortieren - dann ist der Filter wieder kaputt.
    Also LinqToDataset fand ich buggy, und nur für spezielle aufgaben eine Option.

    Ich glaube aber, VPZ meint mit "Filterung des tDS mittels LINQ" was anderes - nicht LinqToDataset. Ich kann mir allerdings nur vorstellen, dass er auf List(Of DataRow) wechselt, und damit ist möglicherwweise die ÄnderungsVerfolgung im Eimer - und mir unklar ist, ob man dann so einem Filtrat noch was zufügen/löschen kann.

    Also ich bin konservativ, und ziehe vorsorglich erstmal in Zweifel, ob Linq im tds eine modernere, flexiblere, bessere Alternative ist zu olle BindingSource.Filter.

    Neu

    ErfinderDesRades schrieb:

    Ich glaube aber, VPZ meint mit "Filterung des tDS mittels LINQ" was anderes - nicht LinqToDataset

    Er wird es uns sicher mitteilen :o)
    Aber es scheint ja festzustehen, dass es sich eher um Geschmackssache, als um besser/schlechter (also BS.Filter und Linq), handelt.

    Ich habe aber noch drei zwei Fragen zum Projekt.
    1. Die Frage mit meiner CCB in auf der frmArtikelBearbeiten (Siehe Vorpost - Frage Nummer1)

    2. (keine Frage merke ich gerade - nur eine Anmerkung) Das Datum. Ich habe mir überlegt, dass ich einen anderen Ansatz wähle. Da es aktuell ja reichlich "Preisanpassungen" gibt, kontrolliere ich an machen Tagen 2 mal täglich meine Rechnungen. Einmal morgens und dann nochmal Nachmittags.
    Wenn ich nun heute morgen und heute Nachmittag Verkaufspreise ändere und den "VK geändert" Filter auf Heute stelle, finde ich alle Artikel deren VK heute geändert wurden.
    Hier wäre es manchmal hilfreich, wenn ich eben Nachmittags, die Artikel von morgens nicht sehe.
    Also habe ich mich entschieden die Uhrzeit im Filter mit anzugeben. Die DTP zur Auswahl habe ich auf Format Custom mit "dd.MM.yy - HH:mm" eingestell und übergebe an meine Suchenklasse wieder das vollständige Datum (mit Uhrzeit).
    Beim ändern eines Artikelpreises wird die Uhrzeit mit gespeichert.
    Es funktioniert alles:

    VB.NET-Quellcode

    1. If _SearchPurchasingPriceChanged Then
    2. If Searchstring = "" Then
    3. Searchstring = $"DatumletzteKalkulationEK >= #{_PurchasingPriceChangedStartDate}# AND DatumletzteKalkulationEK <= #{_PurchasingPriceChangedEndDate}#"
    4. Else
    5. Searchstring = "(" & Searchstring & $") AND DatumletzteKalkulationEK >= #{_PurchasingPriceChangedStartDate}# AND DatumletzteKalkulationEK <= #{_PurchasingPriceChangedEndDate}#"
    6. End If
    7. End If

    Und die eine Zeile zum ändern des EndDatums - PurchasingPriceChangedEndDate = PurchasingPriceChangedEndDate.Date.AddDays(1).AddMinutes(-1) habe ich entfernt. Die brauch ich ja nicht mehr.

    3. In den Projekteigenschaften ist als Startform die Form1 ausgewählt. Diese gibt es nicht mehr, da ich sie in frmHaupt umbenannt habe.
    Aber in der Combobox kann ich keine andere Startform auswählen?

    Und ich lade die aktuelle Solution nochmal hoch, die aus dem Vorpost hatte ja eh noch keinen Download.
    Dateien
    • Theo.zip

      (2,25 MB, 7 mal heruntergeladen, zuletzt: )

    Neu

    Zu Punkt 3: In der Datei My Project\Application.Designer.vb gibt es den Teil, die Du ändern musst:

    VB.NET-Quellcode

    1. <Global.System.Diagnostics.DebuggerStepThroughAttribute()> _
    2. Protected Overrides Sub OnCreateMainForm()
    3. Me.MainForm = Global.Theo.frmHaupt
    4. End Sub


    ##########

    Zu 1: Die CCB ist (noch) nicht dafür gedacht, einen Vorabwert zu haben. Sie hat ja die Aufgabe, Dich zu einer Auswahl zu zwingen. Daher ist von Haus aus das IsEmpty noch True. Aber Du verwendest sie ja hier bei der ArtikelBEARBEITUNG als normale ComboBox. Mir ist klar, dass Du Dir einen Dialog einsparen willst, indem Du Neuanlage und Bearbeitung mit einem Form machst. Aber das ist so nicht in meinen Sinn gewesen. Wenn das Ziel mit dem UndoClear erreichbar ist, ok. Ansonsten musst Du Dich an der CCB-Codequelle zu schaffen machen. Ist ja OpenSource.
    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.

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

    Neu

    Zu 3. Nein, das hilft leider nicht, weil habe ich schon gemacht.
    Bei mir siehts aus wie in deinem Post:

    VB.NET-Quellcode

    1. <Global.System.Diagnostics.DebuggerStepThroughAttribute()> _
    2. Protected Overrides Sub OnCreateMainForm()
    3. Me.MainForm = Global.Theo.frmHaupt
    4. End Sub

    Aber in My Project --> Anwendung kann ich nichts auswählen, als Form1.


    Zu 3. Ich habs nun etwas anders gelöst. Wenn ich im FormLoad Event der Artikelform meine LoadArticleData Sub aufrufe, funktioniert CCBLieferant.UndoClear nicht, weil in der dann ausgeführten Sub "DataSource" nothing ist und diese verlassen wird.
    Ich habe also eine Public Sub innerhalb der CCBKlasse erstellt: SetMeNotEmpty
    und rufe nun folgendes auf:

    VB.NET-Quellcode

    1. CCBLieferant.SetMeNotEmpty()
    2. CCBLieferant.Text = .LieferantRow.Name

    Warum ich an dieser Stelle direkt in die CCB schreiben kann und letztens nicht, verstehe ich nicht ganz, aber es klappt.
    Ist wohl nicht ideal deine CCB "normal" - also mit ISEmpty = True - aufzurufen und diesen Wert dann wieder auf False zu setzen, aber mir fallen viele Stellen ein, wo ich deine CCB "im gedachten" Sinne verwende und nur diese eine hier wos anders ist, deswegen hoffe ich mal, das geht so in Ordnung.

    Aber zwei Fragen habe ich noch (hab ich bisher immer vergessen):
    2. In deiner CCB taucht die Variable DataSource (ohne _) auf. Wo kommt die her? Ich finde keine Deklaration.
    3. Wenn ich innerhalb einer UC auf Code im DataSet zugreifen möchte (z.B. in der UCArtikel Zeile 93 ff) kann ich nicht:
    If Not DtsDaten.Artikel.ArtNrExistsWithinASupplier(NewArtNr, SelectedSupplier) Then ausführen.
    Ich erhalte dann den Fehler: "Der Verweis auf einen nicht freigegebenen MEmber erfordert einen Objeltverweis.
    Da ich mir nicht anders zu helfen wusste habe ich hieraus folgendes gemacht:

    VB.NET-Quellcode

    1. Dim dts = DirectCast(BSMain.DataSource, DtsDaten)
    2. If Not dts.Artikel.ArtNrExistsWithinASupplier(NewArtNr, SelectedSupplier) Then

    Aber geht das nicht auch einfacher?

    Neu

    Habe VS 2022 17.3.0 Preview 6.0, habe Dein Projekt aus Post#168 runtergeladen, da klappt das problemlos mit der Startformeinstellung, siehe Anhang.

    DerSmurf schrieb:

    In deiner CCB taucht die Variable DataSource (ohne _) auf. Wo kommt die her? Ich finde keine Deklaration.
    Aus der Basisklasse, da die CCB eine ComboBox ist: Public Class ClearableComboBox : Inherits ComboBox

    Bzgl UC und tDS: Du hast auf Deinen UCs keine tDS-Instanz, sondern stattdessen die BSMain, also ist der von Dir eingeschlagene Weg genau der richtige.

    Die BSMain ist ja dafür da, damit Du nicht alle vom tDS abhängigen BindingSources umstöpseln musst, also deren DataSource neu setzen musst, wenn Du das Form aufrufst, sondern eben nur die DataSource der BSMain. Ich selbst arbeite ja schon lange nicht mehr mit tDS, habe aber auf jedem Form folgende Herangehensweise:
    • eine DataContainer-Instanz, die alle Daten beinhaltet, analog zum tDS
    • eine DataContainer-BS auf dem Form, an die andere BindingSources koppeln; im FormX.Designer.vb stell ich deren DataSource auf GetType(DataContainer), damit der Designer bescheid weiß und die anderen BindingSources an die Bestandteile des DataContainers koppeln können und Controls dann richtig eingestellt werden können
    • bei SubForm-Instanziierung übergebe ich die DataContainer-Instanz und führe im Startcode des SubForms eben die Umstöpselung der DataContainer-BS-DataSource auf die DataContainer-Instanz durch
    • im Code kann ich direkt auf die DataContainer-Instanz zugreifen und die Daten manipulieren
    Bilder
    • Projekteinstellungen.png

      36,41 kB, 816×619, 6 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.

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