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.

    DerSmurf schrieb:

    Der Zweck ist, dass eine Warengruppe eindeutig sein muss. "Testwarengruppe" darf es genau einmal geben.


    Also bei mir gäb's eine Combobox oder ein DGV, wo ich die Warengruppe auswähle (vorhandene, alphabetisch sortiert) - wird sich ja in Grenzen halten die Anzahl.
    Wenn der gewünschte Eintrag nicht dabei ist, gäb's einen "Neu"-Button, der eben EditNew(Of...) mit entspr. Dialog öffnet.

    Den Dialog nutze ich zur Neuanlage und Bearbeitung und mache beim Schließen eine Prüfung: (achtung, ohne Intellisense grad schnell zusammen geschrieben)

    VB.NET-Quellcode

    1. If Me.DialogResult = DialogResult.OK Then
    2. Me.ValidateChildren
    3. Dim rwWarengruppe = bsWarengruppeDetail.At(Of WarengruppeRow)
    4. If rwWarengruppe.DataRowState = DataRowState.Detached Then 'row ist neu (also editnew)
    5. If dts.warengruppe.any(Function(x)x.Bezeichnung = tbWarengruppe.Text) then messagebox($"Es existiert bereits eine Warengruppe mit Namen {tbWarengruppe.Text}!")
    6. e.cancel = True 'Dialog kann nicht mit OK geschlossen werden, solange der Duplikateintrag da ist. Er kann höchstens abgebrochen werden (DialogResult.Cancel)
    7. End If
    8. End If


    Du hast halt den Vorteil, dass du den Dialog auch für eine Bearbeitung nutzen kannst, dann wird der eben mit EditCurrent(Of...) aufgerufen. Dann haste alles sauber und deine Prüfung auch

    DerSmurf schrieb:

    Anschließend können in den 10 Textboxen die Spalten vergeben werden

    hier hätte ich ne Funktion gebaut, die ne Art Vorlagedatei im Vorfeld ausspuckt bei Bedarf. Bei meinen Import-Methoden müssen die Spaltennamen mit denen in meinem Programm übersteinstimmen, sonst gibt's keinen Import.
    Hat den Vorteil, dass du dann gezielt damit arbeiten und die Werte ordentlich umwandeln kannst in den Zieldatentyp und hast's direkt sauber. Da muss auch niemand mehr was auswählen

    Edit: achja, für die Prüfung ob ne row neu is hab ich mir ne kleine Extension gebaut:

    VB.NET-Quellcode

    1. ''' <summary>Prüft, ob sich eine DataRow gerade in der Erstellung befindet</summary>
    2. <Extension>
    3. Public Function IsNew(rw As DataRow) As Boolean
    4. Return rw.RowState = DataRowState.Detached
    5. End Function


    Edit2: War grad mal im Studio unterwegs mit deinem Anhang. Sähe bei mir so aus:

    VB.NET-Quellcode

    1. Imports System.ComponentModel
    2. Public Class dlgWarengruppeBearbeiten
    3. Private Sub frm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
    4. If Me.DialogResult = DialogResult.OK Then
    5. Me.ValidateChildren()
    6. If TBName.Text.IsEmpty Then
    7. ShowAutoClosingMessageBox("Bitte einen Namen eingeben.")
    8. TBName.Select()
    9. e.Cancel = True
    10. End If
    11. Dim rwWarengruppe = BSWarengruppe.At(Of DtsDaten.WarengruppeRow)
    12. If rwWarengruppe.RowState = DataRowState.Detached Then 'Dialog wurde mit EditNew(Of...) aufgerufen
    13. If DtsDaten.Warengruppe.Any(Function(x) x.Name = TBName.Text) Then
    14. MessageBox.Show($"Es existiert bereits eine Warengruppe mit Namen {TBName.Text}!")
    15. e.Cancel = True
    16. End If
    17. End If
    18. If NUDNummer.Value = 0 Then
    19. ShowAutoClosingMessageBox("Die Warengruppennummer darf nicht 0 sein")
    20. NUDNummer.Select()
    21. e.Cancel = True
    22. End If
    23. End If
    24. End Sub
    25. Private Sub frm_KeyDown(sender As Object, e As KeyEventArgs) Handles MyBase.KeyDown
    26. Select Case e.KeyData
    27. Case = Keys.Enter
    28. SendKeys.Send("{TAB}")
    29. e.SuppressKeyPress = True
    30. End Select
    31. End Sub
    32. End Class


    System.Componentmodel muss importiert werden, für den Eventhandler vom FormClosing, erst dann kannste e.cancel nutzen.
    Können Warengruppen-Nummern mehrfach vorkommen? Sonst müsstest du die auch noch abprüfen...
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

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

    Warengruppennummern dürfen mehrfach vorkommen.
    Aber muss in deiner Version nicht auch das Datenquellen Aktualisierungsintervall auf never?
    Sonst haste dich das gleiche Problem, wie ich.
    Und wenn ich in deiner Version einen Artikel editiere gibt's keine Prüfung.
    Also edit und einen Namen rein, dem es schon gibt, ist möglich.

    Edit: und es bleibt die Frage offen - spricht etwas gegen meine Lösung des Problems?

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

    DerSmurf schrieb:

    Aktualisierungsintervall auf never

    Nö, denn er prüft ja erst beim Schließen des Dialogs. Das reicht meiner Meinung nach auch aus

    DerSmurf schrieb:

    Und wenn ich in deiner Version einen Artikel editiere gibt's keine Prüfung.

    Jo dann mach halt die Prüfung raus, ob die Row neu ist. Dann prüft er immer beim Schließen mit OK bzw. in deinem Fall Save, ob das passt.
    Also Zeile 12,13 und 18 aus meinem Code raus.
    Im Übrigen hatte ich vergessen eben zu sagen: Im Designer in den Eigenschaften deines Dialogs, das DialogResult OK einstellen auf den Save-Button. Dann kannste dir den Klick-Handler sparen


    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

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

    DerSmurf schrieb:

    Und wenn ich in deiner Version einen Artikel editiere gibt's keine Prüfung.
    Also edit und einen Namen rein, dem es schon gibt, ist möglich.


    Da widersprichst du dich ja selbst. Ich seh auch den Sinn dahinter nicht, was zu editieren was aber dann doch so bleibt. Dann brauch' ich ja nix editieren.
    und über abbrechen kommste ja trotzdem aus dem Dialog raus.

    Und das mit dem Enter und Schließen wäre mir jetzt neu. Wenn man durch Tabbed und das ActiveControl dann der Button ist und man Enter drückt, wird soweit ich weiß genauso das "ButtonClicked"-Event ausgelöst, als hätte man das mit der Maus gemacht.
    Zur Not spiel' ein bisschen rum, du kannst am Button selbst auch ein DialogResult setzen. Eine von beiden Varianten (Dialog selbst oder Button) wird wohl für dich funktionieren.

    Edit: hab grad mal bei mir nachgeguckt:
    AcceptButton in den DialogEigenschaften hab ich auch nicht, lediglich CancelButton - kann sein, dass die beiden Settings mit den Tasten Enter und Escape zusammenhängen.
    Dafür hat mein OK-Button die eigenschaft DialogResult OK

    Bei mir kann man damit nix mit Enter schließen (es seidenn der Button OK hätte jetzt Tab-Focus), aber mit Klick auf OK wird das Closing-Event mit entsprechendem Result gefeuert und ich brauch keinen Handler für den Button
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:
    Ah. Dann hängt die Taste Enter mit dem Accept Button zusammen und die Taste Esc mit Cancel Button.

    tragl schrieb:

    Da widersprichst du dich ja selbst. Ich seh auch den Sinn dahinter nicht, was zu editieren was aber dann doch so bleibt.

    Stell dir vor die Warengruppe Namens Futter hat die Nummer 1 und soll nun die Nummer 2 bekommen.
    Dann öffne ich den Dialog - verändere die Nummer und bekomme beim Speicherversuch die Meldung, dass es "Futter" schon gibt.

    Edit: und kannst du mir erklären, was bei meiner Methode schlecht ist?
    Also warum ich sie ändern sollte?

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

    DerSmurf schrieb:

    und kannst du mir erklären, was bei meiner Methode schlecht ist?

    Hab ich nicht behauptet, dass die schlecht ist. Wenn du damit zurecht kommst, dann mach das so. Könntest auch prüfen ob sich die Nummer geändert hat und dann ggf. die Prüfung "abschalten" oder überspringen
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:
    So. Hier mal wieder die aktuellste Version.
    Folgende Fragen habe ich:
    1. Die hier besprochene Kontrolle, ob es einen Eintrag im Dts schon gibt, habe ich auf der dlgWarengruppeBearbeiten, dlg KategorieBearbeiten und der dlgLieferantBearbeiten eingebaut.
    Ist das so ok?
    - auf der frmArtikelBearbeiten
    2. Der Code hier scheint zu laufen - ich habe zumindest keine Fehler festgestellt. Das Einzige was nicht funktioniert ist das Laden eines Lieferanten nach dem Import einer Liste.
    Um den "Fehler" zu reproduzieren habe ich eine banale csv eingefügt. Diese kann importiert werden, wenn der User die Artikel (3. Symbol von links im MenuStrip) öffnet und dort den zweiten Button von rechts (mit dem kleinen Plus Symbol) klickt.
    Es öffnet sich nun die Artikelform mit angezeigtem SCMain.Splitter2 - der sonst ausgeblendet ist). Hier importieren und die Datei auswählen. Anschließend können in den 10 Textboxen die Spalten vergeben werden, in denen die Daten stehen. Alternativ kann (in den Spalten wo es Sinn ergibt), mit einem "=" ein fester Wert belegt werden.
    Also in die SpalteVK z.B: = 10,00 und alle Artikel bekommen einen VK von 10,00€, oder Lieferant =Lieferant1 - alle Artikel bekommen den LIeferanten1.
    Dies funktioniert für alle Textboxen, aber nicht für die ComboBox Lieferant. Es ist also egal was in die TBSPLieferant eingetragen wird, die CBLieferant aktualisiert sich nicht.
    Wenn ich die ComboBox auf DropDownStyle = DropDown umstelle, gehts einwandfrei. Bekommt man das auch mit dem Style DropDownList hin?

    3. Nun hat die Form sehr viele Funktionen. Wenn ich diese Beschreiben müsste: "Das Empfangen von Benutzereingaben zum Neuanlegen, ändern und kopieren eines Artikels, sowie dem Anlegen/Bearbeiten von gespeicherten Artikeln anhand einer Liste, die als csv oder xls(x) importiert werden kann. Sowie dem anschließenden Speichern der Benutzereingabe im DataSet."
    SRP ist also vom Tisch. Sollte ich das ganze in diese Richtung nochmal überarbeiten? Wenn ja könnte ich hier einen Denkanstoß bekommen, was ich wie aufteilen sollte?

    4. Die UCArtikel verfügt über eine aufwändige Suche (Paramter können nach Klick aufs Zahnrad ausgewählt werden), passt die so?

    5. Das ist wohl eher etwas banales - und ich sehe gerade den Wald vor lauter Bäumen nicht - aber, auf der neu erstellten UCPasswort (das Vorhängeschlosssymbol) kann ich BSPasswort.EditNew(Of dlgPasswortBearbeiten) nicht aufrufen.
    Dies wird mir mit dem Fehler quittiert:
    System.ArgumentException: "An die Eigenschaft oder Spalte URL für die DataSource kann nicht gebunden werden.
    Parametername: dataMember"
    . Das passiert auch für die anderen drei gebundenen Textboxen. Was hab ich hier falsch eingestellt?
    Dateien
    • Neues Textdokument.csv

      (27 Byte, 38 mal heruntergeladen, zuletzt: )
    • Theo.zip

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

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

    1. tl;dr bzgl. der Vorgeschichte. Daher überspring ich das mal.
    2. Was muss man machen, nachdem man die csv importiert hat? Wie kann ich jetzt in Deinem Form festlegen, was wohin importiert werden soll? Was muss ich da in die TextBoxen eintragen? Der Import besteht ja nur aus
    Spalte 1
    Spalte 2
    Spalte 3
    Spalte 4
    Spalte 5
    11
    12
    13
    14
    15
    21
    22
    23
    24
    25

    3. Ich werf man n Blick drauf.
    4. dito.
    5. Tja, da biste irgendwo in den Tiefen eine Fremdanbieters (alias EdR). Da musste den fragen. Da hab ich irgendwie keine Muße, mich reinzufitzeln.

    btw: Ich dächt, dass EAN inzwischen zu GTIN umbenannt wurde …
    Und: Im Filter von UcArtikel hat sich wohl Dein Comicinteresse breit gemacht. Das Teil heißt nicht Asteriks, sondern Asterisk. Auch wenn ich mal vor Jahren gelesen habe, dass Asterix und Obelix tatsächlich ihre Namen von den Worten Asterisk und Obelisk bekommen haben sollen …

    ##########

    Die Suche ist tatsächlich komplex und m.E. auch in eine Extraklasse auslagerbar.
    nochmal ein btw: Ich glaube immer noch, dass Article der falsche Begriff ist, auch wenn EdR der festen Überzeugung zu sein scheint, dass es richtig ist. Article wird aber m.E. für Zeitungsartikel oder Einzelteile verwendet. Für Komplettprodukte heißt es m.E. Item. Aber nachdem Du es schon so weit durchgezogen hast …

    Zu 3. Warum nicht einfach die Kurzbeschreibung: Es ist ne Einzelartikelverwaltung bzw. Artikelbearbeitung. Damit hast Du doch die Aufgabe m.E. gut beschrieben. Dass die Aufgabe komplex ist, ist richtig. Aber sie macht ja nicht total abwegige Sachen wie Bestandskontrollisten drucken, Bestellungen auslösen oder sonstewas. Ja, manchmal ist es so, dass ein Form auch komplexe Aufgaben macht. Da zwangsweise was zu zerhackstückeln, wäre nicht sinnvoll. Oder überlegst Du was von der Funktionalität auszulagern?
    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 4 mal editiert, zuletzt von „VaporiZed“ ()

    Hallo und vielen Dank für deine Antwort :o)

    Ist das bei 1. eine Abkürzung die ich nicht kenne, oder hat dich die Autokorrektur erwischt?

    VaporiZed schrieb:

    tl;dr bzgl. der Vorgeschichte

    2. siehe Screentshot:
    Nach dem Import kannst du in den Textboxen, über dem DGV die Spaltennummern eintragen, wo was steht.
    Also im Screenshot Name in Spalte 1 und Lieferant in Spalte 2.
    Wenn du nun einen Eintrag im DGV auswählst, werden die Daten in die ensptrechende Textbox (bei den Artikelinfos) eingetragen.
    Also nach Klick in die erste DGV Zeile steht bei Name "11", würde ich in die zweite DGV Zeile klicken stünde bei Name "21".
    Genauso soll das auch beim Lieferanten möglich sein. Das ist es aber nur, wenn ich den DropDownStyle auf DropDown umstelle, mit DropDownList, passiert einfach nix.

    3. Ich wollte nur generell wissen, ob ein Auslagern hier sinnvoll ist, oder die Sache nur unnötig kompliziert. In meiner alten Anwendung hatte ich eine Form zum Artikel bearbeiten und eine (mehr oder weniger identische) Form zum importieren einer csv.
    Das war natürlich ziemlich doof, weil im Prinzip beide Forms den gleichen Code beinhalten. Daher ist es jetzt eine. Aufgrund der Menge des Codes hatte ich nur Sorge, dass dies too much ist.
    Laut meinem Buch soll eine Beschreibung der Funktion einer Form (oder Klasse, ...) kurz und knapp möglich sein, ohne und. Das ist ja hier nicht der Fall - für mich ist die Form trotzdem sinnvoll, wie sie ist und wenn ich die Funktionalität mit Artikelverwaltung beschreibe, ist doch auch alles schön.

    4. (und deine Unterpunkte :o)
    - Du hast absolut recht. EAN heißt GTIN. Aber niemand, wirklich niemand in meiner Branche nennt die Zahlenfolge GTIN. Es ist immer (in Gesprächen, in der Kasse, in Preislisten) von EAN die Rede.
    - Das Article, bzw. item Problem gehe ich morgen an. Zwar sehe ich das so, wie EdR, aber ich kenne jemanden, der ein paar Jahre für eine Englische Firma gearbeitet hat. Der müsste es ja wissen.
    Also morgen benenne ich entweder um, oder belehre dich.
    - DieSucheInEineKlasseAuslagern: Ich habe nicht so recht Ahnung, wie ich das anstellen soll. Aber ich mach mich mal ran. Mal sehen, was ich dir dann präsentieren kann xD

    5. Da mach ich dann gleich einen neuen Thread auf und adressiere den Verantwortlichen direkt :o)

    Edit: Den Rechtschreibefehler bei unserem gallischen Freund habe ich natürlich berichtigt. Asterix Name wird nun nicht mehr in meinem Code verschandelt.

    DerSmurf schrieb:

    eine Abkürzung die ich nicht kenne
    tl;dr

    2. Was'n für'n Screenshot?
    Ou, falsche Spalte eingetragen (6), schon ein Crash.
    Argh, nee, die Importgeschichte ist für einen Außenstehenden nicht instinktiv. Ich blick da leider nicht durch, daher weiß ich auch nicht, wie ich zum Problem vorstoßen kann.
    Und bei Steuer ist auch keine DropDownList, daher kann ich da auch sonstewas eintragen.

    3. Belass es erstmal dabei. Später kannst Du immer noch versuchen, Teile in eigene Klassen auszulagern, die das Form dann nutzt. Am Ende wär es natürlich super, wenn das Form nur GUI-Änderungen vornimmt und User-Eingaben weiterleitet und sonst nur andere Klassen die Arbeit machen. Aber das jetzt auf Biegen und Brechen zu erreichen ist nicht zielführend.

    4. Jou, wird bei uns auch genauso weiterhin EAN genannt.
    Article/Item: bin gespannt.
    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.
    Sorry, den Screenshot hatte ich vergessen. Und den Fehler beseitige ich natürlich noch.
    Im Screenshot steht in der NameTB (über dem DGV) eine 1 (rot umkringelt). Das bedeutet in der Importierten Liste stehen die Name in Spalte 1.
    Es ändert sich die entsprechende DGV Überschrift (Pfeil nach unten). Wenn ich nun im DGV eine Zeile markiere, wird der entsprechende Wert in die NameTB (oben) eingetragen (Pfeil nach oben.
    Das passiert beim Lieferanten nicht (auch nicht, wenn ich einen Lieferanten "importiere" den es gibt.

    Bei Steuer ist mit Absicht (noch) keine DropDownList. Das erinnert mich daran, dass die Steuer noch im Programm - durch den Benutzer änderbar - hinterlegt werden müssen.
    Ich wollte allerdings erstmal die "alte" Funktionalität am Laufen haben bevor ich neue Sachen hinzufüge. Ich glaube das wird nämlich nicht ganz trivial.

    Und die Frage hinter 1. geht am einfachsten am Beispiel (und ist so natürlich 1zu1 auf alle weiteren Verwendungen übertragbar).
    Wir sind auf der dlgLieferantBearbeiten. Hier erstellt und ändert der User Lieferanten.
    Es darf natürlich jeden Lieferanten nur einmal geben, daher eine Prüfung beim speichern, ob der Lieferant schon existiert.
    Die Schwierigkeit war, dass diese Prüfung nicht auch beim ändern anschlägt.
    Wenn ich Lieferant1 bearbeite und eine neue Kundennummer hinterlege, darf natürlich keine Meldung kommen, dass es Lieferant1 schon gibt.
    Wenn ich aber versuche den Lieferant1 in Lieferant2 umzubennen, und es den Lieferanten2 schon gibt, muss eine Meldung kommen:
    Die Frage ist nun, ob meine Lösung so in Ordnung ist:

    VB.NET-Quellcode

    1. Public Class dlgLieferantBearbeiten
    2. Private _OldSupplierName As String
    3. Private Sub dlgLieferantBearbeiten_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    4. Dim CreateNewSupplier = Me.BSLieferant.At(Of DtsDaten.LieferantRow).RowState = DataRowState.Detached
    5. If Not CreateNewSupplier Then _OldSupplierName = Me.BSLieferant.At(Of DtsDaten.LieferantRow).Name
    6. End Sub
    7. Private Sub BTNSave_Click(sender As Object, e As EventArgs) Handles BTNSave.Click
    8. Dim SupplierName = TBName.Text
    9. If SupplierName.IsEmpty Then
    10. ShowAutoClosingMessageBox("Bitte einen Namen eingeben")
    11. Return
    12. End If
    13. If _OldSupplierName <> SupplierName AndAlso DtsDaten.Lieferant.SupplierExists(SupplierName) Then
    14. ShowAutoClosingMessageBox($"Der Lieferant {SupplierName} existiert bereits")
    15. Return
    16. End If
    17. SaveSupplier()
    18. End Sub
    19. End Class
    Bilder
    • Screenshot 2022-08-02 084646.png

      129,76 kB, 2.404×1.632, 39 mal angesehen
    So. Ich habe die Artikelsuche in eine eigene Klasse verfrachtet. Dabei sind mir erst meine immer gleichen Wiederholungen des Codes aufgefallen. Die Suchengeschichte sieht nun so aus:

    VB.NET-Quellcode

    1. Private Sub BuildSearchstringAndSearchArticle()
    2. Dim bs = GetUsedBindingsource()
    3. Dim SearchStringBuilder As New BildenDesArtikelSuchString(TBArtikelDurchsuchen.Text, RBGenau.Checked, CBXArtNr.Checked, CBXEAN.Checked,
    4. CBXName.Checked, CBXZusatz.Checked, CBXEK.Checked, CBXRabatt.Checked, CBXLP.Checked, CBXSpanne.Checked,
    5. CBXVK.Checked, CBXNotiz.Checked, CBXAusgelaufen.Checked, CBXVKGeaendert.Checked, DTPVKStart.Value,
    6. DTPEKend.Value, CBXEKGeaendert.Checked, DTPEKstart.Value, DTPEKend.Value)
    7. bs.Filter = SearchStringBuilder.BuildSearchstring
    8. End Sub

    und die Suchenklasse (Solution hänge ich auch noch an)
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class BildenDesArtikelSuchString
    2. Private _Searchtext As String
    3. Private _Asterisk As String
    4. Private _SearchArtNo As Boolean
    5. Private _SearchEAN As Boolean
    6. Private _SearchName As Boolean
    7. Private _SearchNameAddition As Boolean
    8. Private _SearchPurchasingPrice As Boolean
    9. Private _SearchDiscount As Boolean
    10. Private _SearchListPrice As Boolean
    11. Private _SearchSalesMargin As Boolean
    12. Private _SearchRetailPrice As Boolean
    13. Private _SearchNote As Boolean
    14. Private _SearchExpiring As Boolean
    15. Private _SearchRetailPriceChanged As Boolean
    16. Private _RetailPriceChangedStartDate As String
    17. Private _RetailPriceChangedEndDate As String
    18. Private _SearchPurchasingPriceChanged As Boolean
    19. Private _PurchasingPriceChangedStartDate As String
    20. Private _PurchasingPriceChangedEndDate As String
    21. Public Sub New(Searchtext As String, SearchExact As Boolean, SearchArtNo As Boolean, SearchEAN As Boolean, SearchName As Boolean, SearchNameAddition As Boolean,
    22. SearchPurchasingPrice As Boolean, SearchDiscount As Boolean, SearchListPrice As Boolean,
    23. SearchSalesMargin As Boolean, SearchRetailPrice As Boolean, SearchNote As Boolean, SearchExpiring As Boolean,
    24. SearchRetailPriceChanged As Boolean, RetailPriceChangedStartDate As Date, RetailPriceChangedEndDate As Date,
    25. SearchPurchasingPriceChanged As Boolean, PurchasingPriceChangedStartDate As Date, PurchasingPriceChangedEndDate As Date)
    26. _Searchtext = Searchtext
    27. If SearchExact Then
    28. _Asterisk = ""
    29. Else
    30. _Asterisk = "*"
    31. End If
    32. _SearchArtNo = SearchArtNo
    33. _SearchEAN = SearchEAN
    34. _SearchName = SearchName
    35. _SearchNameAddition = SearchNameAddition
    36. _SearchPurchasingPrice = SearchPurchasingPrice
    37. _SearchDiscount = SearchDiscount
    38. _SearchListPrice = SearchListPrice
    39. _SearchSalesMargin = SearchSalesMargin
    40. _SearchRetailPrice = SearchRetailPrice
    41. _SearchNote = SearchNote
    42. _SearchExpiring = SearchExpiring
    43. _SearchRetailPriceChanged = SearchRetailPriceChanged
    44. _SearchPurchasingPriceChanged = SearchPurchasingPriceChanged
    45. If _SearchRetailPriceChanged Then
    46. _RetailPriceChangedStartDate = RetailPriceChangedStartDate.ToString("MM/dd/yyyy HH:mm:ss").Replace(".", "/")
    47. RetailPriceChangedEndDate = RetailPriceChangedEndDate.Date.AddDays(1).AddMinutes(-1)
    48. _RetailPriceChangedEndDate = RetailPriceChangedEndDate.ToString("MM/dd/yyyy HH:mm:ss").Replace(".", "/")
    49. End If
    50. If _SearchPurchasingPriceChanged Then
    51. _PurchasingPriceChangedStartDate = PurchasingPriceChangedStartDate.ToString("MM/dd/yyyy HH:mm:ss").Replace(".", "/")
    52. PurchasingPriceChangedEndDate = PurchasingPriceChangedEndDate.Date.AddDays(1).AddMinutes(-1)
    53. _PurchasingPriceChangedEndDate = PurchasingPriceChangedEndDate.ToString("MM/dd/yyyy HH:mm:ss").Replace(".", "/")
    54. End If
    55. End Sub
    56. Public Function BuildSearchstring() As String
    57. Dim Searchstring As String
    58. If _Searchtext.IsNotEmpty Then
    59. If _SearchArtNo Then Searchstring = AddParamterToSearchString(Searchstring, "ArtNr")
    60. If _SearchEAN Then Searchstring = AddParamterToSearchString(Searchstring, "EAN")
    61. If _SearchName Then Searchstring = AddParamterToSearchString(Searchstring, "Name1")
    62. If _SearchNameAddition Then Searchstring = AddParamterToSearchString(Searchstring, "Zusatz")
    63. If _SearchPurchasingPrice Then Searchstring = AddParamterToSearchString(Searchstring, "Convert(Einkaufspreis, System.String)")
    64. If _SearchDiscount Then Searchstring = AddParamterToSearchString(Searchstring, "Convert(Rabatt, System.String)")
    65. If _SearchListPrice Then Searchstring = AddParamterToSearchString(Searchstring, "Convert(Listenpreis, System.String)")
    66. If _SearchSalesMargin Then Searchstring = AddParamterToSearchString(Searchstring, "Convert(Marge, System.String)")
    67. If _SearchRetailPrice Then Searchstring = AddParamterToSearchString(Searchstring, "Convert(Verkaufspreis, System.String)")
    68. If _SearchNote Then Searchstring = AddParamterToSearchString(Searchstring, "Notiz")
    69. End If
    70. If _SearchExpiring Then
    71. If Searchstring = "" Then
    72. Searchstring = "auslauf = true"
    73. Else
    74. Searchstring = "(" & Searchstring & ") AND auslauf = true"
    75. End If
    76. End If
    77. If _SearchRetailPriceChanged Then
    78. If Searchstring = "" Then
    79. Searchstring = $"DatumletzteKalkulationVK >= #{_RetailPriceChangedStartDate}# AND DatumletzteKalkulationVK <= #{_RetailPriceChangedEndDate}#"
    80. Else
    81. Searchstring = "(" & Searchstring & $") AND DatumletzteKalkulationVK >= #{_RetailPriceChangedStartDate}# AND DatumletzteKalkulationVK <= #{_RetailPriceChangedEndDate}#"
    82. End If
    83. End If
    84. If _SearchPurchasingPriceChanged Then
    85. If Searchstring = "" Then
    86. Searchstring = $"DatumletzteKalkulationEK >= #{_PurchasingPriceChangedStartDate}# AND DatumletzteKalkulationEK <= #{_PurchasingPriceChangedEndDate}#"
    87. Else
    88. Searchstring = "(" & Searchstring & $") AND DatumletzteKalkulationEK >= #{_PurchasingPriceChangedStartDate}# AND DatumletzteKalkulationEK <= #{_PurchasingPriceChangedEndDate}#"
    89. End If
    90. End If
    91. Return Searchstring
    92. End Function
    93. Private Function AddParamterToSearchString(Searchstring As String, ParamterName As String) As String
    94. If Searchstring = "" Then
    95. Searchstring = $"{ParamterName} Like '{_Asterisk}{_Searchtext}{_Asterisk}'"
    96. Else
    97. Searchstring &= $"OR {ParamterName} LIKE '{_Asterisk}{_Searchtext}{_Asterisk}'"
    98. End If
    99. Return Searchstring
    100. End Function
    101. End Class

    Mir ist noch keine Möglichkeit eingefallen, wie ich die letzten drei Suchabfragen (Notiz, DatumEKÄnderung, DatumVKÄnderung) in die AddParamterToSearchString Funktion integrieren kann.
    Deswegen habe ich es erst mal so gelassen. Ist ja insgesamt schon deutlich besser also vorher (denke ich).

    VaporiZed schrieb:

    Ou, falsche Spalte eingetragen (6), schon ein Crash.

    Der Fehler ist behoben.

    Außerdem habe ich jetzt mal ernsthaftere Testdaten in die Artikel und auch in die import csv eingetragen.
    Außerdem wird nach dem Import der csv in die Artikel eine Sub ausgeführt, welche die Daten für dich einträgt, du brauchst also nur die csv importieren und im DGV rumklicken.
    Dann siehst du, wie sich oben (bei den Artikelinfos) die Daten ändern. (Sorry, auf die Idee, dir diese Arbeit abzunehmen, hätte ich auch mal eher kommen können...)
    So kannst du aber nun problemlos diesen Fehler nachstellen. Wenn du den letzten Artikel im DGV anklickst ("Namens klick mich") wird der Lieferant in der ComboBox nicht geändert.
    Er müsste aber entweder leer werden, oder den Namen aus dem DGV annehmen, damit ich nicht versehentlich den Artikel einem falschen Lieferanten zuordne.
    Hier bietet sich ja deine ClearableComboBox an (denke ich), aber kann ich diese auch problemlos ohne den "x" Button verwenden? Oder würdest du das an dieser Stelle anders lösen?

    Außerdem bitte ich dich mal in die dlgLieferantBearbeiten zu schauen. Meine Prüfung ob es einen Lieferanten schon gibt, haut nämlich nicht hin. Aber ich stehe auf dem Schlauch:

    VB.NET-Quellcode

    1. Private _OldSupplierName As String
    2. Private Sub dlgLieferantBearbeiten_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    3. Dim CreateNewSupplier = Me.BSLieferant.At(Of DtsDaten.LieferantRow).RowState = DataRowState.Detached
    4. If Not CreateNewSupplier Then _OldSupplierName = Me.BSLieferant.At(Of DtsDaten.LieferantRow).Name
    5. End Sub
    6. Private Sub BTNSave_Click(sender As Object, e As EventArgs) Handles BTNSave.Click
    7. Dim SupplierName = TBName.Text
    8. '{...}
    9. If _OldSupplierName <> SupplierName AndAlso DtsDaten.Lieferant.SupplierExists(SupplierName) Then
    10. ShowAutoClosingMessageBox($"Der Lieferant {SupplierName} existiert bereits")
    11. Return
    12. End If
    13. SaveSupplier()
    14. End Sub

    If _OldSupplierName <> SupplierName AndAlso DtsDaten.Lieferant.SupplierExists(SupplierName) Then
    Die zweite Prüfung ist bei mir immer True. Wo ist hier mein Denkfehler?
    Zum nchklicken (auf Zahnrad im MenuStrip und dann auf Lieferanten und bearbeiten)
    Was ich vor habe:
    Wenn ich den Lieferantennamen ändere, soll geprüft werden, ob es den neuen Namen schon gibt.
    Wenn sich der Lieferantenname nicht ändert, gibts nicht zu prüfen.

    2Edits:
    @VaporiZed der Englischmann ist im Urlaub und erst nächste Woche wieder im Büro.
    Die Antwort reiche ich dir also nach.

    Und falls du über das Speichern von Zugangsdaten und EmailZugangsdaten im Dts stolperst - das DataSet wird am Ende der Übung hier natürlich noch verschlüsselt.

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

    Private Sub PrepareStuffForVaporized() :D
    Also gut, das mit der CBLieferant klappt so nicht, weil Du ja nicht den Text setzen kannst. Ist ja kein beschreibbares Control als DropDownList. Da musst Du schon eine spezifische Subroutine (oh mann, dass ich tatsächlich mal dieses Klischeewort verwende …) schreiben, die die CB direkt ändert.

    AddParamterToSearchString - das Wort Parameter hast Du häufiger im Code falsch geschrieben.

    DerSmurf schrieb:

    Hier bietet sich ja deine ClearableComboBox an (denke ich), aber kann ich diese auch problemlos ohne den "x" Button verwenden?
    Der X-Button ist seit langem nur ergänzend. In manchen Programmen habe ich ihn drin, in manchen nicht. Bei letzterem wäre ein Szenario:
    Bearbeiter: [(keine Auswahl getroffen) v] - und dann klickt man die CCB an, es werden die relevanten Mitarbeiter aufgelistet und sofort der erste gewählt. Wenn man aber vergisst, die CCB anzuklicken, meckert das Programm, dass eben noch kein Mitarbeiter ausgewählt wurde.

    ##########

    Beim Einstellungsdialog werden TabReiter angezeigt. Absicht?

    ##########

    Wieder das DataBindingproblem. Da die TextBox an die Daten des aktuellen Lieferanten gebunden ist und aufgrund der Datenquellenaktualisierung die TextBoxdaten in die Datenquelle übernommen werden, änderst Du quasi live den Namen. Und der ist dann bei der Überprüfung somit schon vergeben. Setz den Aktualisierungmodus auf Never und speicheren nach der Prüfung den Namen manuell:

    VB.NET-Quellcode

    1. Private Sub SaveSupplier()
    2. BSLieferant.At(Of DtsDaten.LieferantRow).Name = TBName.Text
    3. Me.DialogResult = DialogResult.OK
    4. End Sub

    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 4 mal editiert, zuletzt von „VaporiZed“ ()

    Den Anhang im Vorpost mit dem Lieferantennamen bei der Bearbeitung des Lieferanten hattest Du gelesen?

    DerSmurf schrieb:

    Das vergessene Parameter "e" habe ich überall hinzugefügt.
    ?( Mir ging es um das A: ParAmeter.
    Die CCB nutze ich eigentlich immer indirekt über die BindingSource. Ich setze also deren Current und mach nix in der CCB direkt.
    Die SuchStringBildenKlasse hab ich mir nicht angeschaut, weil ich seltenst mit BS-Filterstrings arbeite. Ich ändere immer die BS-DataSource :rolleyes:

    ##########

    Das PurchasingPriceChangedEndDate = PurchasingPriceChangedEndDate.Date.AddDays(1).AddMinutes(-1) in Kombination mit DatumletzteKalkulationVK <= #{_RetailPriceChangedEndDate}#" versteh ich nicht. Also doch, ich versteh es schon. Aber es geht doch einfacher, wenn Du mit Dates arbeitest und/oder DatumletzteKalkulationVK < #{_RetailPriceChangedEndDate}#" schreibst. Also nix kleiner gleich.
    Mit »mit Dates arbeiten« meine ich: Wenn Du ein DateTime-Pack speicherst, speichere es nicht einfach so, sondern immer die Date-Komponente davon. Dann kommst Du immer bei Mitternacht raus und Datumsvergleiche werden einfacher, weil Du eben die Uhrzeit nicht beachten musst. Was hab ich mir früher n Wolf gesucht, weil ich nicht wusste, warum irgendwelche Datumsvergleiche Müll ergeben. Nur, um dann immer wieder festzustellen, dass die mitgespeicherte Uhrzeit meine Vergleiche zerschossen hat.

    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“ ()

    VaporiZed schrieb:

    Mir ging es um das A: ParAmeter.

    Sorry, das meinte ich.
    Aber diese Sub ist doch Teil der Klasse SuchStringBildenKlasse oO

    und hier mein Post von vorhin:
    Ja wie gesagt, die Sub PrepareStuffForVaporized kommt zu spät. Hätte dir wohl einiges wahlloses rumgeklicke erstpart.

    Ich habe nun die ComboBox in eine ClearableComboBox verwandelt, damit eben kein Lieferant (also nichts) angezeigt werden kann, wenn der Lieferant nicht gefunden wird.
    Nach dem klicken ins DGV führe ich nun noch folgende Sub aus:

    VB.NET-Quellcode

    1. Private Sub WriteSupplier(RowIndex As Integer, ColumnTextBox As TextBox)
    2. Dim ColumnIndex As Integer
    3. Dim Dts = DirectCast(BSMain.DataSource, DtsDaten)
    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. If Dts.Lieferant.SupplierExists(DGVArtikel.Rows(RowIndex).Cells(ColumnIndex).Value.ToString) Then
    8. CCBLieferant.Text = DGVArtikel.Rows(RowIndex).Cells(ColumnIndex).Value.ToString
    9. Else
    10. CCBLieferant.Clear()
    11. End If
    12. End If
    13. End If
    14. End If
    15. End Sub

    Damit sollte die CCB ja leer sein, wenn der Lieferant nicht existiert. (Aktuell ist sie aber immer leer - das bekannte Problem)
    Beim speichern der Daten prüfe ich dann zusätzlich noch CCBLieferant.Isempty.

    Ich verstehe aber nicht, warum beim Laden der Daten:

    VB.NET-Quellcode

    1. Private Sub LoadArticleData()
    2. _CancelALLPriceEvents = True
    3. With _SelectedArticle
    4. CCBLieferant.Text = .LieferantRow.Name
    5. '...
    6. End Sub

    Der korrekte Lieferant in der CCB angezeigt wird (Wenn ich diese Zeile auskommentiere, wird immer der erste angezeigt - also ein falscher)
    Aber in meiner WriteSupplier Sub funktioniert das nicht?
    Aber gut, wenn ich eine spezifische Subroutine (fangen wir mal an das Wort zu etablieren :o) erstelle, muss ich dann die Items der CCBLieferant.List durchsuchen und den SelectedIndex setzen? Oder geht das einfacher?

    Das vergessene Parameter "a" habe ich überall hinzugefügt. Sonst hast du nichts zu meckern an der SuchStringBildenKlasse?
    Dann begreife ich ja langsam was :o)

    edit: und nochmal die Solution mit CCB

    Edit: die Tabreiter in den Settings sind Absicht
    Ich muss ja irgendwie durch die TapPages navigieren können und finde diese Art der Reiter optisch gar nicht doof.
    Dateien
    • Theo.zip

      (2,25 MB, 22 mal heruntergeladen, zuletzt: )
    • Neues Textdokument.csv

      (220 Byte, 19 mal heruntergeladen, zuletzt: )

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

    Hallo lieber @lululuhai21
    Was möchtest du uns mit deinem Post sagen?

    @VaporiZed Das Mückenproblem ist jetzt gelöst. Ich gehe erstmal meine offenen Probleme an (sind mir gerade etwas viele) und melde mich, wenn ich fertig bin.
    Es sind ja genügend Denkanstöße in deinen letzten beiden Posts.
    Aber eine Frage habe ich vorab. Du sagt, du nutzt kein BS.Filter, sondern nutzt die BS Datasource.
    Hast du hierzu evtl. eine Demoanwendung mit einer ähnlichen Suche, damit ich gucken kann was du damit meinst?

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