Bindingsource und DialogForm

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 35 Antworten in diesem Thema. Der letzte Beitrag () ist von Akanel.

    Bindingsource und DialogForm

    Hallo zusammen,

    ich versuche mich daran ein Rechnungsprogramm zu erstellen ( aus Übungszwecken).
    Grundlage war das Tutorial von @ErfinderDesRades MiniKass.
    Ich wollte das ganze etwas ausbauen, und dabei lernen.

    Dabei habe ich folgendes Dataset:


    und auf der Form selbst schaut es so aus:


    Nun ist es so dass ich eine Rechnung zu dem entsprechenden Kunden anlegen kann ( via Button) und dann im DataGridView zu der Rechnung die Entsprechenden Artikel zufüge.
    Dazu habe ich im PostenDGV die Artikelspalte wie folgt geändert.


    Nun möchte ich aber das ganze, ich sage mal beschönigen, und eine DialogForm zum erstellen der Rechnung inclusive der zugehörigen Artikel.


    Jetzt habe ich allerdings das Problem das dass ändern der Artikelspalte auf der Form nicht so funktioniert.
    Es werden mir Zwar die ID´s angezeigt, aber ich möchte wieder den Artikelnamen haben. Kein Mensch kann sich die ID zum gehörigen Namen merken.

    Wo habe ich da meinen Denkfehler bzw was habe ich vergessen oder falsch gemacht?

    So sieht Form1 zur Laufzeit aus:


    MfG Sven
    Rechtschreibfehler betonen den künstlerischen Charakter des Autors.

    Akanel schrieb:

    das dass
    http://www.dass-das.de/

    Akanel schrieb:

    ändern der Artikelspalte auf der Form nicht so funktioniert.
    In welcher Anzeige funktioniert das Ändern nicht? Bzw was meinst du mit funktioniert nicht? Kannst du im Designer den DisplayMember (also den Namen der Property, welche im UI angezeigt werden soll) nicht einstellen?

    Akanel schrieb:

    Es werden mir Zwar die ID´s angezeigt, aber ich möchte wieder den Artikelnamen haben.

    Akanel schrieb:

    So sieht Form1 zur Laufzeit aus:
    Also wenn ich mir deine Anwendung so ansehe, dann steht doch im DGV für die Posten keine Artikelnummern/ArtikelIDs, sondern Artikelbezeichnungen drinnen, passt doch? Oder verstehe ich dich hier falsch??

    Lg Radinator
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    Du möchtest nicht dass der Kunde die dgv manuell befüllt sondern hast dafür eine seperate Form ja? Wenn ja, dann könntest du der neuen Form einfach paar sachen übermitteln, am besten in den constructor deiner neuen form:

    VB.NET-Quellcode

    1. CType(deinebindingsource.Current, DataRowView).Row.Field(Of datatyp)(columnindex)
    Hier könnte meine Signatur stehen.
    @Radinator
    Auf Form1 habe ich unten das PostenDGV welches die Property ArtikelID abruft. DisplayMember ist hier allerdings der ArtikelName. Damit das funktioniert hatte ich ja die Bindingsource umgestöpselt. Siehe Bild 3 im ersten Post.
    Das ganze möchte ich nun auch auf der DialogForm haben. Und das funktioniert nicht.
    Ich bekomme es dort nicht hin das der DisplayMember auch der ArtikelName ist.

    Ist es jetzt etwas verständlicher ausgedrückt?
    Rechtschreibfehler betonen den künstlerischen Charakter des Autors.

    Akanel schrieb:

    Ich bekomme es dort nicht hin das der DisplayMember auch der ArtikelName ist.
    Zeig uns mal bitte den Designer Dialog für das DGV der DialogForm, wie in Bild #3 in deinem ersten Post.
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    Das ist das gleiche. Weil die Daten ja in der Postenbindingsource sind. Nur für die Spalte Artikel dachte ich muss ich wieder die Artikelbindingsource nehmen, da dort ja der Artikel Name hinterlegt ist den ich angezeigt haben möchte.
    Der einzige Unterschied ist halt das es auf einer anderen Form als Dialog ist.

    Edit: Ich habe mal die Solution angehangen wo derzeit das Problem besteht.
    Dateien
    • Umsatz.rar

      (212 kB, 113 mal heruntergeladen, zuletzt: )
    Rechtschreibfehler betonen den künstlerischen Charakter des Autors.

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

    Wenn man davon absieht, dass ich in Deinem Code nicht ganz klar komme, warum da PostenBindingSource und ArtikelBindingSource z.B. in der Hauptform z.B. im Posten-DGV irgendwie vermischt sind oder in der mit "ArtikelID" beschrifteten Spalten Artikelnamen kommen und ich noch nicht ganz durchgestiegen bin, wo Du gern was anders haben willst, geb ich mal folgenden Hinweis: Wenn Du in Deiner Rechnungsdialogform (Name "dlgRechnung", Formtitel "Rechnung") im Posten-DGV in der Spalte namens "ArtikelID" (warum auch immer diese Spalte diesen Titel trägt) Deine Artikelnamen ("Betriebsanweisung erstellen", Arbeitsanweisung erstellen, ...) drin stehen haben willst, musst Du in Form1 in der Region Dialoge im Sub RechnungBearbeiten vor die Zeile If frmRechnung.ShowDialog(Me) = DialogResult.OK Then noch eine weitere BindingSource umstöpseln, und zwar mit frmRechnung.ArtikelBindingSource.DataSource = ArtikelBindingSource. Dann tauchen Deine Artikel im genannten DialogForm-DGV auf.

    btw: "Arbeitsplatz begehung 1. UE" "Arbeitsplatzbegehung 1. UE"
    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.
    Ich hab mal eine funktionierende Umstöpselung eingebaut.
    Beachte, dass ich in dlgRechnung das üflüssige Dataset weggeräumt hab, und eine bsMain zwischen die lokalen BindingSources und dem verbliebenen lokalen Dataset eingefügt hab.
    Auf diese Weise mussich beim Umstöpseln nur eine BindingSource anfassen.
    Ich glaub, was mit Umstöpseln gemeint ist, und der Sinn davon, ist nicht klar.
    Das Problem ist, dass dlgRechnung im Designer ein eigenes Dataset erhält - identisch zu dem in Form1, aber eben eine andere Instanz. Wenn nun Form1 sein Dataset lädt, weiß ja dlgRechnung nix davon, sondern dessen Dataset bleibt ja leer. Und alle seine BindingSources, und das ganze schöne Design ist and dieses leere Dataset angeschlossen - bringt logischerweise nix.
    Daher - bevors ühaupt angezeigt wird - erstmal Umstöpseln aller BindingSources auf das Form1.DAtaset - wie gesagt, durch meine eingeschobene Bindingsource besonders einfach:

    VB.NET-Quellcode

    1. Private Sub RechnungBearbeiten()
    2. Using frmRechnung As New dlgRechnung
    3. frmRechnung.bsMain.DataSource = Me.Dts ' alle Dialog-BindingSources aufs Form1-Dataset umleiten
    Aber das ist auch alles erklärt in Daten laden, speichern, verarbeiten - einfachste Variante (versucht zu erklären wenigstens).
    Ausserdem nutze ich dort einen viel einfacheren Weg, nämlich eine von mir gebastelte HelperSolution, die die Umstöpselei automatisch (und sogar bisserl gründlicher) durchführt.


    Das weitere

    VB.NET-Quellcode

    1. frmRechnung.RechnungBindingSource.DataSource = RechnungBindingSource.Current ' zu editierende DataRow setzen
    ja gut - ist auch ein Umstöpseln, aber das stöpselt nur die zu editierende BindingSource auf einen bestimmten Datensatz um - es leitet nicht die Gesamtheit des BindingSource-Designs des Edit-Dialogs auf's Form1-Dataset um.



    Beachte aber auch, dass bei so komplexen Bearbeitungs-Dialogen ein Teil des Dialog-Patterns verloren geht: Canceln kannst du nur, was via RechnungBindingSource im RechnungsDatensatz eingetragen wurde.
    Aber in deim Dialog machst du ja auch Einträge inne RechnungPosten-Tabelle - diese Veränderungen wirken sofort ins Dataset (das siehste auch), und lassen sich später durch einfaches Canceln auch nicht mehr revidieren, denn .RechnungBindingSource.CancelEdit() wirkt ja nicht auf die Posten-Tabelle.

    Da gibts auch kein Patentrezept für (ausser das PostenGrid readonly machen).
    Also für die Bearbeitung der angewählten RechnungRow ist das ja ein Patentrezept, nach dem Prinzip kann man alle möglichen angewählten Rows bearbeiten.
    Aber wenn im 'Dialog komplexe Eingaben vorgesehen sind, mehrere Tabellen betreffend - wie hier gezeigt ist das durchaus möglich, aber das Standard-Cancel-Feature mit BindingSource.CancelEdit geht dabei flöten.
    Muss man mit leben, oder halt je nach Einzelfall besonderen Aufwand treiben, um im Cancel-Falle dennoch "zurückrudern" zu können.
    Dateien
    • Umsatz00.zip

      (46,91 kB, 119 mal heruntergeladen, zuletzt: )

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

    Vielen Dank für deine Hilfe, nun funktioniert es wie es soll und ich habe es endlich auch geschnallt.

    Du hast es sehr wohl in deinem Tutorial erklärt, in Post Nr. 7, ich war halt nur zu dämlich es zu verstehen.

    Deine HelperSolution habe ich mir schon angesehen und mich daran versucht. Das Hätte mit ziemlicher Sicherheit auch funktioniert.
    Allerdings fehlt es mir noch etwas an Verständnis das ich mir solche komplexen Sachen einbaue, am ende aber nicht verstehe was da überhaupt passiert.
    Daher werde ich noch etwas Zeit brauchen bevor ich mich wirklich daran traue.

    Das weitere Problem was du beschreibst mit dem Canceln der Einträge ist mir bewusst, aber eine wirkliche Lösung dazu ist mir bisher auch noch nicht eingefallen.
    Aber Du hast ja schon einige Möglichkeiten aufgezeigt die ich probieren werde.
    Ich denke dazu werde ich mit Sicherheit noch die eine oder andere Frage haben.
    Rechtschreibfehler betonen den künstlerischen Charakter des Autors.

    Akanel schrieb:

    solche komplexen Sachen einbaue, am ende aber nicht verstehe was da überhaupt passiert.
    Daher werde ich noch etwas Zeit brauchen bevor ich mich wirklich daran traue.
    Jo weiss ich auch nicht.
    Natürlich hast du bereits (etwa mitte WinForms-dll) Tonnen aüsserst komplizierten Codes eingebaut, von dem du nicht die Bohne verstehst - oder meinst du, zu verstehen, was in einer Textbox vor sich geht?
    .Net coden heisst, Dinge richtig benutzen zu lernen, und sich um die Innereien nicht kümmern zu müssen.

    Ist - da geb ich dir recht - eine heikle Sache bei so selbstgebastelten Sachen - also eine Release von MS ist in der Tat überaus viel vertrauenswürdiger als meine Sammlung komischer Helferlein.
    Aber dennoch sind auch meine Helferlein nicht unbedingt zum Verstehen vorgesehen, sondern machen ihren Job (sollen sie wenigstens) auch, wenn man den Code darin (noch) nicht nachvollziehen kann.
    Das Verständnis der Helpers-Innereien ist eine Art "möglicher Luxus" der daher rührt, dass du die Sources einbindest, nicht nur die kompilierte Dll.
    Naja richtig wissen was im Hintergrund alles so passiert tue ich nicht. Ich dachte auch eher, wenn ich schon Code von anderen Nutze sollte ich wissen was dieser bezweckt, und wie er es tut. Es ging mir da auch nicht um eine Vertrauenssache. Ich denke nicht das es an deinem Code was auszusetzen gibt, nur verstehen möchte ich ihn.
    Ich teste es einfach aus, vielleicht habe ich MIT der Nutzung des Codes einen besseren Lerneffekt.

    Was ich aber gerade noch getestet habe, wenn ich eine neue Rechnung anlege, einen Artikel wähle und dann die Anzahl eingebe wirft es mir plötzlich folgenden Fehler aus:



    Da muss irgendwas falsch sein was beim anlegen einer neuen Rechnung passiert. Hängt wohl mit AddNew() zusammen, weil bereits bestehende Rechnungen kann ich bearbeiten und auch Artikel problemlos hinzufügen oder löschen.
    Rechtschreibfehler betonen den künstlerischen Charakter des Autors.

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

    ja, das hängt auch mit dem Cancel-Feature zusammen: Bindingsource.Addnew erzeugt eine nur vorläufige Datarow (mit Datarowstate.Detached).
    Damit die Zufügung mit .CancelEdit auch wieder ungeschehen gemacht werden kann.
    Bei soner detachten Datarow kannste natürlich keine ChildRows adden, weil was soll denn mit denen passieren, wenn die RechnungRow gecancelt wird?

    Lösung: die RechnungRow sofort committen. Dann kann sie aber nur noch ordnungsgemäss deleted wern, ein Bs.CancelEdit ist nicht mehr möglich:

    VB.NET-Quellcode

    1. Case sender Is btnRechnungNeu
    2. RechnungBindingSource.AddNew() : RechnungBindingSource.EndEdit() : RechnungBearbeiten()
    Es Funktioniert zwar, aber ich denke das es da auch noch eine andere Lösung geben muss.
    Sobald ich nun eine Rechnung anlege, wird ja die Bearbeitung der RechnungBindingSource mit RechnungBindingSource.EndEdit() abgeschlossen.
    Das bedeutet wiederum das ich daran nichts mehr ändern kann, kein Datum (LieferDatum, RechnungsDatum).

    Ich meine in einem Deiner Beispiele hast du etwas ähnliches, wohl aber mit Deiner HelpersSolution. Ich meine es war OrdersVB.
    Das sollte ich mir vielleicht mal genauer anschauen.

    Das wird wieder was zum Lernen habe ich das gefühl.
    Rechtschreibfehler betonen den künstlerischen Charakter des Autors.

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

    Akanel schrieb:

    Sobald ich nun eine Rechnung anlege, wird ja die Bearbeitung der RechnungBindingSource mit RechnungBindingSource.EndEdit() abgeschlossen.
    Das bedeutet wiederum das ich daran nichts mehr ändern kann, kein Datum (LieferDatum, RechnungsDatum).
    Doch doch - ändern kannste die Datensätze jederzeit.
    Ist nur dann ein weiterer Edit-Vorgang.
    Wennde mein Code-Vorschlag ausprobierst siehste ja auch, dass noch geht. Nur das Canceln ist gecancelt - bzw. bs.CancelEdit() stellt halt nicht auf den Zustand vor .Addnew() der DataRow zurück, sondern auf den Zustand nach .EndEdit().
    Etwa könntest du beim Canceln die Row nun explizit löschen, um das AddNew aufzuheben. Das geht durchaus, und die Lösschweitergabe entfernt auch die untergeordneten Posten.
    Einzig ist, dasses nicht mehr so schön ist, dass man händisch was dazu-coden muss, während in simplen Szenarien der IEditableObject-Pattern sich automatisch um alles kümmert.
    Oh man da habe ich mich auf was eingelassen.
    Das hört sich leichter an als es für mich ist.

    ​Ist nur dann ein weiterer Edit-Vorgang.

    Heisst wohl ich muss einen weiteren Edit-Vorgang dazu programmieren.

    Und wie ich dann aus dem Dialog mittels des Cancel Buttons den aktuell angelegten Datensatz wieder lösche ist mir derzeit auch noch nicht klar.

    Aber ich werde es probieren.

    Danke
    Rechtschreibfehler betonen den künstlerischen Charakter des Autors.

    Akanel schrieb:

    Heisst wohl ich muss einen weiteren Edit-Vorgang dazu programmieren.
    Nein, du hast garnix damit zu tun.
    Ich habe nur versucht, zu erklären, warum du beim Hinzufügen mittels bs.AddNew() gleich bs.EndEdit() aufrufen musst, bevor das EditForm ühaupt anzeigt - siehe mein Code.
    Und dass das für die Bindingsource dann 2 Edit-Vorgänge sind: beim ersten fügt .AddNew() eine vorläufige Datarow hinzu, das folgende .EndEdit übernimmt diese Zufügung sogleich als abgeschlossenen Edit-Vorgang, und danach erst öffnet sich das EditForm und editiert den nun bereits übernommenen Datensatz. Und letztere Editierung kann der User canceln, aber das stellt den Vorgang nur soweit zurück auf den Stand nach Übernahme der Hinzufügung, und das widerspricht dem eiglichen IEditableObject-Pattern.
    Weil das Hinzufügen + Editieren einer einfachen DataRow, bei der nicht Childrows mit-zu-editieren sind - solche Zufügungen sind eiglich cancellable.

    Oder anners: Der Cancel-Button auf deim Editieren-Form cancelt nicht richtig - dassis alles.
    Ok das habe ich nun verstanden.

    .AddNew() legt eine neue DataRow an, .EndEdit() schließt die Bearbeitung sofort ab. Die DataRow ist somit schon da.
    Dann wird die EditForm aufgerufen und bearbeitet den zuvor eingefügten Datensatz, der ja noch leer ist.

    Aber das RechnugsDatum und das LieferDatum werden somit nicht übernommen. Auch wenn ich nachher nochmal den Datensatz über das Form bearbeiten möchte, wird alles korrekt ausgeführt, nur halt die beiden Datumse nicht.

    Ich habe nochmal die Solution angehangen damit du es nachvollziehen kannst.

    Edit: Es Funktioniert, allerdings erst nachdem ich die Bindingsources alle neu auf die die EditForm gezogen habe. Quasi das Form neu erstellt habe.
    Macht das Sinn, oder hätte das auch so funktionieren müssen?
    Dateien
    • Umsatz EdR.rar

      (203,51 kB, 108 mal heruntergeladen, zuletzt: )
    Rechtschreibfehler betonen den künstlerischen Charakter des Autors.

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

    Akanel schrieb:

    Aber das RechnugsDatum und das LieferDatum werden somit nicht übernommen.
    Die werden deshalb nicht übernommen, weil die entsprechenden Datatimepicker kein Databinding haben - zumindest nicht in der Solution, die ich grad gedownöoaded hab.
    Ich würd dich auch SolutionExplorer - OpenSource empfehlen, der zippt eine Solution ohne Binaries.
    Oder machs händisch, dasss du den bin-ordner iwie anners aus der Zipdatei entfernst - ist nämlich aus guten Gründen nicht erlaubt, ausführbare Dateien hier übers Forum in Umlauf zu bringen.
    Oh man, da habe ich mich nun ja mal völlig blamiert. Ich habe echt gesucht wie ein dummer Esel, aber daran habe ich nicht gedacht. Dabei sollte das als erstes gemacht werden, den die beiden DTP waren ja die Betroffenen Controls.

    Nochmals danke für den Hinweis auf den SolutionExplorer. Ich habe den ja bereits, nur nicht dran gedacht ihn für den Upload zu nutzen.

    Jedenfalls Vielen Dank für deine Hilfe.
    Rechtschreibfehler betonen den künstlerischen Charakter des Autors.
    @ErfinderDesRades
    Ich muss das Thema nochmal aufgreifen.
    Ich habe nun versucht das ganze mit deinen "Helpers" umzusetzen.

    Habe es auch soweit hinbekommen, allerdings ist mir einiges aufgefallen wo ich den Fehler nicht finde.
    Also mit folgendem Code rufe ich das DialogForm dlgRechnung auf.

    VB.NET-Quellcode

    1. Private Sub RechnungErstellen(editNew As Boolean)
    2. Dim rwRechnung As RechnungRow
    3. Dim RechnungPosten As PostenRow() = {}
    4. If editNew Then
    5. Dim result = bsKundeRechnung.EditNew(Of dlgRechnung)(RechDts.Rechnung.RechnungsDatumColumn, Date.Today)
    6. If result <> DialogResult.OK Then Return
    7. rwRechnung = bsKundeRechnung.At(Of RechnungRow)() ' holt die neu erstellte RechnungRow
    8. Else
    9. rwRechnung = bsKundeRechnung.At(Of RechnungRow)() 'holt die aktuelle RechnungRow
    10. RechnungPosten = rwRechnung.GetPostenRows
    11. If bsKundeRechnung.EditCurrent(Of dlgRechnung)() <> DialogResult.OK Then Return
    12. End If
    13. End Sub

    Diese Form beinhaltet 2 DateTimePicker und das DataGridView der Posten (um die Artikel hinzuzufügen)
    Zu Testzwecken habe ich mir das DataGridView der Rechnung auch mal auf die Form gezogen.

    Der Wert des DTP für das RechnungsDatum wird ja schon Codeseitig gesetzt.
    Den Wert für das LieferDatum setzte ich nun mit dem zweiten DTP.

    Schaue ich mir das nun im DGV der Rechnungen an wird bei dem RechnungsDatum "24.05.2017" angezeigt. Im LieferDatum hingegen wird mir noch die Uhrzeit dahinter angegeben.
    Das ganze ergibt dann eine InvalidContraintException


    Setze ich das Datum hingegen auch mit dem DTP und nehme das Codeseitige setzen raus, erscheinen in beiden Spalten Datum mit Uhrzeit.
    Wieder die Exception.
    Wenn ich nun aber die Zeit aus beiden Feldern in DGV manuell lösche, funktioniert das ganze.


    Das Problem ist, ich weiß nicht wieso das ganze ohne die Zeit funktioniert und mit nicht.

    In deinem Beispiel OrdersVB funktioniert es ja auch mit den DateTimePickern. Im dem .xml von OrdersVB habe ich mir das auch mal angesehen. Da steht dann überall als Zeitangabe 00:00:00.

    Dann dachte ich es liegt an dem NULLABLEDATETIMEPICKER von dir, war es scheinbar aber nicht, denn damit bekomme ich auch die Fehler.
    Rechtschreibfehler betonen den künstlerischen Charakter des Autors.