TableLayoutPanel zur Laufzeit mit COntrols befüllen

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

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

    TableLayoutPanel zur Laufzeit mit COntrols befüllen

    Hallo Leute
    Ich bastel gerade an einer Erweiterung meines Programmes um Rechnungen und Angebote zu erstellen.
    Dazu habe ich in einem TableLayoutPanel ein Panel eingefügt, wo wiederum ein TableLayoutPanel liegt. Hierauf habe ich eine Reihe Controls im Designer erzeugt (siehe Screenshot).
    Ohne TLP habe ich die Controls nicht ordentlich angeordnet bekommen, wenn das Programm zwischen Fenstermodus und Vollbild wechselt.
    Nach einem Klick auf den Button "+" sollen die gleichen Controls in der leeren TLP Zeile eingefügt werden und eine neue TLP Zeile angelegt werden.
    Wie sortiere ich meine Controls in das TLP?
    So sieht das ganze aus, wenn ich die Controls ohne Container auf die Form male:

    VB.NET-Quellcode

    1. Private Sub DrawControls()
    2. Dim yPosition As Integer = 190
    3. Dim xPosition As Integer = 120
    4. 'TextBox
    5. Dim newTB = New System.Windows.Forms.TextBox
    6. With newTB
    7. .Location = New System.Drawing.Point(xPosition, yPosition)
    8. .Font = New Font("Microsoft Sans Serif", 12)
    9. .Name = "TBAmount"
    10. .Size = New System.Drawing.Size(120, 26)
    11. .Visible = True
    12. 'AddHandler .TextChanged, AddressOf TextChangeEvent
    13. ' AddHandler .Enter, AddressOf TextBox_Enter
    14. End With
    15. 'TBList.Add(newTB)
    16. 'Controls auf die Form malen
    17. Me.Controls.Add(newTB)
    18. End Sub
    Bilder
    • Unbenannt.png

      132,16 kB, 1.920×1.080, 101 mal angesehen
    Stop. Wenn Du unterschiedliche Controls dem TLP hinzufügen willst, ist das ok. Wenn Du immer wieder die gleichen Controls in der gleichen Reihenfolge hinzufügen willst (Button runter, TextBox, Button rauf, lange TextBox, TextBox, TextBox, PlusButton, MinusButton), dann pack die genannten Controls in ein UserControl und füge immer ein neues UserControl dem TLP (was Du dann ggf. gar nicht mehr brauchst) hinzu.
    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.
    Ja, überzeugt. Das Userconrol bekommt dann einen Anchor links und rechts und ich kann mir das TLP sparen.
    Damit verschieben sich dann auch meine weiteren Fragen. Ich muss erstmal UserControl googlen xD.

    Aber ich kann ja in einem UserControl auch Code einfügen, damit erübrigt sich auch gleichzeitig das Problem, dass ich irgendwie handlen muss, welcher Button, welche Textbox steuert...
    Korrekt. Wenn ein UserControl (UC) quasi eine Bestellzeile repräsentiert, kannst Du im UC allen Code für diese Bestellzeile hinterlegen. Dann musst Du Dich im UC nur mit dieser einen Bestellzeile beschäftigen.
    Ggf. noch DataBinding dazu. Und noch GUI-Daten-Trennung/MVC-Style … :rolleyes: aber ich schweife ab.
    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 tue mein bestes. Mir fällt jetzt noch nicht ein, wie ich die Daten nachher abfrage.
    Also z.B. alle Gesamtpreise der einzelnen Zeilen (2x Hundefutter a 3,00€ = 6,00€), aber das bekommen wir schon hin.
    Ich schreibe jetzt erstmal den Code für meine Controls und erstelle das UC.
    Nur frage ich mich, sollte ich hier hier Properties im UC anlegen, für die Daten, dich ich nachher in der Mainform brauche?

    Edit: Verzeih - hier kommen mehr Infos. In der einen Zeile auf dem Screenshot siehst du (von links nach rechts):
    - Button zum verrringern der Menge um 1
    - TextBox zum anzeigen der Menge
    - Button zum erhöhen der Menge um 1
    - Textbox zur Anzeige des Artikelnamens
    - Textbox für den Einzelpreis
    - Textbox für den Gesamtpreis
    - Button zum erstellen einer neuen "Zeile" (diesen werde ich nicht in die UC integrieren, sondern einen einzigen auf die "normale" Form klatschen
    - Button zum löschen der "Zeile"

    Zum erstellen der Rechnung brauche ich nun die Daten, für die ich unten schon labels vorbereitet habe (Neeto 7%, Netto 19% usw.).
    Hierfür brauche ich ja quasi die Daten aus meinen UserControls.
    Wenn ich das ganze richtig verstanden habe, kann ich diese wie aus einer Form holen - also über Properties.
    Ich gebe also dem UC die benötigten Properties (Netto19, Netto7, usw.), damit ich diese auslesen kann und in der Mainform speichern kann.

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

    So. Ich habe jetzt den Code auf der normalen Form erstellt und würde genauso meine Userform erstellen.
    Kannst du mal rüber gucken, ob das so Sinn macht? Ich hoffe die Benennung ist selbsterklärend und der Code kurz genug, um ihn ohne Anwendung zu verstehen.
    Wenn nicht erstell ich ein Demoprojekt und lade was hoch.

    Spoiler anzeigen

    VB.NET-Quellcode

    1. 'Propertys für UC - damit ich die Daten leicht rausholen kann
    2. Private _ArticlePrice As Double
    3. Private _ArticleName As String
    4. Private _ArticlePriceTotal As Double
    5. Private _ArticleAmount As Integer
    6. Private _CancelEvent As Boolean = False
    7. 'Hier würde ich natürlich noch "gescheite" Properties erstellen. Also Public Property _ArticlePrice as double
    8. Private Sub BTNDrecrease_Click(sender As Object, e As EventArgs) Handles BTNDrecrease.Click
    9. ChangeAmount(-1)
    10. End Sub
    11. Private Sub BTNIncrease_Click(sender As Object, e As EventArgs) Handles BTNIncrease.Click
    12. ChangeAmount(+1)
    13. End Sub
    14. Private Sub ChangeAmount(Increasement As Integer)
    15. Dim amount As Integer
    16. If Integer.TryParse(TBAmount.Text, amount) Then
    17. amount += Increasement
    18. TBAmount.Text = amount.ToString
    19. _ArticleAmount = amount
    20. Else
    21. amount += Increasement
    22. TBAmount.Text = amount.ToString
    23. _ArticleAmount = amount
    24. End If
    25. CalculatePriceTotal()
    26. End Sub
    27. Private Sub TBAmount_KeyPress(sender As Object, e As KeyPressEventArgs) Handles TBAmount.KeyPress
    28. Select Case Asc(e.KeyChar)
    29. Case 48 To 57, 8
    30. ' Zahlen, Backspace und Space zulassen
    31. Case Else
    32. ' alle anderen Eingaben unterdrücken
    33. e.Handled = True
    34. End Select
    35. End Sub
    36. Private Sub TBAmount_KeyUp(sender As Object, e As KeyEventArgs) Handles TBAmount.KeyUp
    37. If TBAmount.Text = "" Then
    38. _ArticleAmount = 0
    39. Else
    40. _ArticleAmount = CInt(TBAmount.Text)
    41. End If
    42. CalculatePriceTotal()
    43. End Sub
    44. Private Sub TBName_DoubleClick(sender As Object, e As EventArgs) Handles TBName.DoubleClick
    45. 'ruft eine neue Form auf, auf der die Artikel ausgewählt werden können
    46. 'aus der Bindingsource kommen dann Artikelname und Preis
    47. Dim ArticleSearch As New frmArticleSearch
    48. With ArticleSearch
    49. .DtsSettings = Me.DtsSettings
    50. .ArticleBindingSource.DataSource = Me.DtsSettings
    51. .ShowDialog()
    52. If .DialogResult = DialogResult.OK Then
    53. _ArticleName = ._Articlename
    54. _ArticlePrice = ._ArticlePrice
    55. _ArticlePriceTotal = ._ArticlePrice
    56. End If
    57. End With
    58. If _ArticleAmount = 0 Then
    59. _ArticleAmount = 1
    60. TBAmount.Text = "1"
    61. End If
    62. TBName.Text = _ArticleName
    63. TBPrice.Text = _ArticlePrice.ToString("#,##0.00 €")
    64. CalculatePriceTotal()
    65. End Sub
    66. Private Sub TBPrice_TextChanged(sender As Object, e As EventArgs) Handles TBPrice.TextChanged
    67. If _CancelEvent Then
    68. _CancelEvent = False
    69. Else
    70. If TBPrice.Text = "" Then
    71. _ArticlePrice = 0
    72. Else
    73. _ArticlePrice = CDbl(TBPrice.Text)
    74. End If
    75. CalculatePriceTotal()
    76. End If
    77. End Sub
    78. Private Sub TBPrice_LostFocus(sender As Object, e As EventArgs) Handles TBPrice.LostFocus
    79. _CancelEvent = True
    80. TBPrice.Text = _ArticlePrice.ToString("#,##0.00 €")
    81. End Sub
    82. Private Sub CalculatePriceTotal()
    83. _ArticlePriceTotal = _ArticleAmount * _ArticlePrice
    84. TBTotalPrice.Text = _ArticlePriceTotal.ToString("#,##0.00 €")
    85. End Sub
    86. Private Sub BTNDelete_Click(sender As Object, e As EventArgs) Handles BTNDelete.Click
    87. _ArticlePrice = 0
    88. _ArticleName = ""
    89. _ArticlePriceTotal = 0
    90. _ArticleAmount = 0
    91. TBAmount.Text = ""
    92. TBName.Text = ""
    93. TBPrice.Text = ""
    94. TBTotalPrice.Text = ""
    95. End Sub
    Bin grad an ähnlichem dran. Ich beschreibe mal eben grob wie ich es umgesetzt habe, evtl. hilft es ja:

    Hab eine DataGridView als Grundlage genommen.

    Dann habe ich mir eine Klasse Position erstellt. Dort finden alle Berechnung auf Positionsebene statt (Menge x EP = GP). Aus dieser Klasse habe ich eine List(Of Position) erzeugt die als DataSource im DataGridView hängt.

    Die Gesamtsumme wiederum wird per Schleife durch meine Liste berechnet.

    Per event melden die Positionen wenn sich was geändert hat, was die neuberechnung der Gesamtsumme auslöst.
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen

    mrMo schrieb:



    Dann habe ich mir eine Klasse Position erstellt. Dort finden alle Berechnung auf Positionsebene statt (Menge x EP = GP). Aus dieser Klasse habe ich eine List(Of Position) erzeugt die als DataSource im DataGridView hängt.

    Die Gesamtsumme wiederum wird per Schleife durch meine Liste berechnet.

    Per event melden die Positionen wenn sich was geändert hat, was die neuberechnung der Gesamtsumme auslöst.

    Eiglich alles Dinge, die bei einer typDataTable gleich OnBord gewesen wären.
    Na ich habs doch eigentlich recht ähnlich, wie @mrMo
    Ich nutze kein DGV sondern eben das UC. Die Klasse Position ist bei mir dann ebenfalls das UC.

    Ich habe auch kurz drüber nachgedacht statdessen in meinem Dts eine DataTable anzulegen und ein gebundenes DGV zu nutzen.
    Aber ich finde so rein optisch, die Controls einfach sehr viel schöner als ein DGV.

    Nun würde ich meine UserControls (mit in Post 6 gezeigtem Code) erstellen und zur Laufzeit auf die Form semmeln, wenn der User auf den Plus Button drückt. Bei der Erstellung des UCs würde ich dies ebenfalls in einer List(of ...) speichern.
    Dann kann ich ja bestimmt, bei der Erzeugung dieser UCs ein Change Event erstellen, welches ja dann auf alle Controls eines UCs reagieren müsste.
    Wird dieses Event gefeuert, durchschleife ich meine List(of ...) und ziehe mir die Daten aus den Properties der UCs.
    Dafür müsste ich aber noch ein paar mehr Properties erstellen, fällt mir gerade ein. (Mwst. Netto19, Netto7, usw.) müssten ja auch in den UCs gehalten werden.

    Passt dieser Ansatz so, oder ist das totaler Murks? Weils viel einfacher - effiezienter - besser - geht?
    Wie ist die Performance bei mehreren 100 UC Positionen bei dir?
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    Ich habe keine Ahnung.
    Weil ich nicht so recht weiß was ich hier tue, wollte ich erst nachhorchen, ob das überhaupt Sinn macht.

    Ich habe also bisher nur (auf der normalen Form) die Controls eingefügt und den Code erstellt.
    Diesen habe ich noch nicht in eine UC umgewandelt.

    Da du aber nach der Performance fragst, nehme ich mal an mein Ansatz ist nicht so verquer.
    Ich mache also heute/morgen Abend mal weiter und dann bekommst du auch eine Antwort :o)
    Soho. Sorry, ich bin nicht so ganz mit dem Code klar gekommen. Nun habe ich ein UserControl erstellt und kann dieses per Code auf die Form malen. Das klappt soweit.
    So sieht das aus (ein allererster Entwurf):

    VB.NET-Quellcode

    1. Private _xPos As Integer = 10
    2. Private _yPos As Integer = 10
    3. Private _UCCount As Integer = 0
    4. Private _UCList As New List(Of Control)
    5. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    6. 'UC erstellen
    7. Dim newUC = New UCInvoice
    8. With newUC
    9. .DtsSettings = Me.DtsSettings
    10. .ArticleBindingSource.DataSource = Me.DtsSettings
    11. .Location = New System.Drawing.Point(_xPos, _yPos)
    12. .Name = "TB " & _UCCount
    13. .TabIndex = _UCCount + 1
    14. .Visible = True
    15. 'AddHandler .TextChanged, AddressOf TextChangeEvent
    16. AddHandler .Validated, AddressOf test1
    17. End With
    18. _UCList.Add(newUC)
    19. 'Control auf die Form malen
    20. Me.Panel2.Controls.Add(newUC)
    21. 'yPos anpassen
    22. _yPos += 50
    23. End Sub


    Nun brauche ich aber ein Event, welches feuert, wenn sich der Text in irgendeiner TextBox meines UserControls, ändert.
    Ich habe gefühlt alle ausprobiert, aber das einzige was halbwegs funktioniert ist das Validated Event, welches aber erst ausgelöst wird, wenn die TextBox in der UserControl verlassen wird.
    Ich möchte gerne mit diesem Event die Properties des UserControls an die Form übergeben - und das wenn möglich schon bei der Eingabe, um z.B. die gesamt Rechnungssumme neu auszurechnen.

    Edit: Dazu noch eine Info (damit ihr diesmal alles wisst :)
    Es ist möglich, dass der User Artikel durch schreiben in die Textboxen eingibt. Alternativ kann er aber auch die Name TB doppelklicken, um folgenden Code auszuführen:

    VB.NET-Quellcode

    1. Private Sub TBName_DoubleClick(sender As Object, e As EventArgs) Handles TBName.DoubleClick
    2. 'ruft eine neue Form auf, auf der die Artikel ausgewählt werden können
    3. 'aus der Bindingsource kommen dann Artikelname, -Nr und Preis
    4. Dim ArticleSearch As New frmArticleSearch
    5. With ArticleSearch
    6. .DtsSettings = Me.DtsSettings
    7. .ArticleBindingSource.DataSource = Me.DtsSettings
    8. .ShowDialog()
    9. If .DialogResult = DialogResult.OK Then
    10. _ArticleName = ._Articlename
    11. _ArticleNumber = ._ArticleNumber
    12. _ArticlePrice = ._ArticlePrice
    13. _ArticlePriceTotal = ._ArticlePrice
    14. End If
    15. End With
    16. If _ArticleAmount = 0 Then
    17. _ArticleAmount = 1
    18. TBAmount.Text = "1"
    19. End If
    20. TBArtNR.Text = _ArticleNumber
    21. TBName.Text = _ArticleName
    22. TBPrice.Text = _ArticlePrice.ToString("#,##0.00 €")
    23. CalculatePriceTotal()
    24. End Sub


    Diese Werte können dann aber natürlich - durch Beschreiben der Textboxen - geändert werden.

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

    Entweder Du abonnierst im MainForm das TextChanged-Event der UserControl-TextBoxen (Aufbruch der Kapselung, also net so dolle und auch aufwendig) oder Du erstellst im UC ein eigenes Event, welches Du im MainForm registrierst und das gefeuert wird, wenn sich bei ner TextBox was tut. Oder Du arbeitest mit INotifyPropertyChanged.

    VB.NET-Quellcode

    1. 'statt Zeile#18
    2. AddHandler newUC.TextBoxesChangedTheirContent, AddressOf test1
    3. 'in Deiner UC-Klasse
    4. Public Class UCInvoice
    5. Public Event TextBoxesChangedTheirContent As EventHandler
    6. Private Sub TextBox_TextChanged(sender As Object, e As EventArgs) Handles Txt1.TextChanged, Txt2.TextChanged, Txt2.TextChanged
    7. RaiseEvent TextBoxesChangedTheirContent(me, EventArgs.Empty)
    8. End Sub
    9. '…

    so aus'm Kopp heraus
    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.
    Und auch ausm Kopp heraus, hast dus drauf. Vielen Dank!

    @mrMo die Frage zur Performance, beantworte ich dir in den nächsten Tagen. Hab dich nicht vergessen.
    Aber ganz im Ernst, wenn ich das ganze nochmal mache, überlege ich mir doch stark eine DataTable mit den benötigen Artikelinfos anzulegen und ein gebindetes DGV zu verwenden.

    und noch eine Frage @VaporiZed

    VaporiZed schrieb:

    Ggf. noch DataBinding dazu. Und noch GUI-Daten-Trennung/MVC-Style … aber ich schweife ab.

    Habe deinen Post fleißig reingezoomt und konnte auch das hier erkennen :o)
    Ich bin mir zumindest sicher, dass ich dies eingehalten habe. Also zumindest GUI Daten Trennung (Berechnung in Properties und dann den Inhalt der Properties in LBL oder TB anzeigen) und ich glaube auch MVC habe ich beachtet und eingehalten.
    Aber DataBinding habe ich komplett ignoriert.
    Die benötigten Daten halte ich in Properties der UC vor. Wenn im Programm eine neue Instanz der UC angelegt wird, wird diese in einer List(of UserControl) gespeichert.
    Diese List of durchschleife ich dann, um auf die Properties zuzugreifen - also den Artikelpreis abhängig von der Mwst, z.B. so:

    VB.NET-Quellcode

    1. For Each item As UCInvoice In _UCList
    2. If item._Tax = 19 Then
    3. Brut19 += item._ArticlePriceTotal
    4. Else
    5. Brut7 = item._ArticlePriceTotal
    6. End If
    7. Next


    Wie würdest du das machen? Bzw. wie wäre der bessere Weg mit DataBinding?
    Edit: hier noch die Properties der UC (wobei ich hier glaube noch ein paar entfernen werde, weil ich sie doch nicht brauche)
    Public Property _ArticlePrice As Double
    Public Property _ArticleName As String
    Public Property _ArticleNumber As String
    Public Property _ArticlePriceTotal As Double
    Public Property _ArticleAmount As Integer
    Public Property _CancelEvent As Boolean = False
    Public Property _Net19 As Double
    Public Property _Net7 As Double
    Public Property _Tax As Integer

    Edit2: gerade habe ich festgestellt, dass ich "von außen" ja noch auf meinen "löschen Button" zugreifen können muss.
    Das habe ich nun genauso erledigt, wie das Change Event:

    VB.NET-Quellcode

    1. 'UC
    2. Public Event DellButtonPressed As EventHandler
    3. Private Sub BTNDelete_Click(sender As Object, e As EventArgs) Handles BTNDelete.Click
    4. RaiseEvent DellButtonPressed(Me, EventArgs.Empty)
    5. End Sub
    6. 'Mainform beim erstellen der UC Instanz
    7. AddHandler newUC.DellButtonPressed, AddressOf DeleteUC
    8. Private Sub DeleteUC(sender As Object, e As EventArgs)
    9. Dim klickedUC = DirectCast(sender, UCInvoice)
    10. RemoveHandler klickedUC.TextBoxesChangedTheirContent, AddressOf UCChangeEvent
    11. RemoveHandler klickedUC.DellButtonPressed, AddressOf DeleteUC
    12. Me.Controls.Remove(klickedUC)
    13. klickedUC.Dispose()
    14. _UCList.Remove(klickedUC)
    15. End Sub


    passt das so, oder macht man das bei Button Klicks anders?

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

    VB.NET-Quellcode

    1. If item._Tax = 19 Then
    2. Brut19 += item._ArticlePriceTotal
    3. Else
    4. Brut7 = item._ArticlePriceTotal
    5. End If
    Kleiner Fehler bei Brut7 ;)
    Das mit dem Delete-Button passt an sich. Mit DataBinding bekommst Du Deine Berechnung nicht hin, DataBinding ist ja dafür da, dass eben GUI und Datenlogik so miteinander arbeiten, dass man da nix mehr manuell anzeigen oder Eingaben aus dem GUI ziehen muss.
    Aber Trennung GUI und Daten sieht etwas anders aus. Da mach ich grad intensive Erfahrungen in der WPF. Grundsätzlich ist es nur so, dass eben ein Form oder UserControl gar nix an Datenlogik enthalten sollte, sondern nur Änderungen entgegen nimmt oder eben an eine Datenklasse weitergibt - eben mithilfe von DataBinding. Und die Datenklasse kümmert sich um Berechnungen etc.
    Ich frag mich, was Du unter MVC verstehst, wenn Du sagst, dass Du das beachtest.
    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:

    Kleiner Fehler bei Brut7

    Oh, bin ich garnicht drüber gestolpert, weil ich schlauerweise immer mit einem 7% und einem 19% Artikel getestet habe.

    VaporiZed schrieb:

    Grundsätzlich ist es nur so, dass eben ein Form oder UserControl gar nix an Datenlogik enthalten sollte, sondern nur Änderungen entgegen nimmt oder eben an eine Datenklasse weitergibt

    Nun das ist bei mir nicht so. Es ist alles in der UC Klasse. Allerdings sind immerhin, die Berechnungen klar von Eingabe und Ausgabe getrennt.
    Das ist ja für mich auch schon mal ein riesen Fortschritt *grins*

    VaporiZed schrieb:

    Ich frag mich, was Du unter MVC verstehst, wenn Du sagst, dass Du das beachtest.

    Ich habe den Wikipedia Artikel so verstanden, dass es wichtig ist, dass es im Grunde eine klare Trennung zwischen Eingabe, Berechnung und Ausgabe geben muss.
    Ich kann meinen Code problemlos in diese drei Gruppen gliedern:
    Eingabe = TBChangeEvents und Button Klicks
    Berechnung = Die eine Sub, die meine Gesamtsumme ausrechnet (und in Property speichert)
    Ausgabe = Die Eine Sub, die die Gesamtsumme Property in eine TB schreibt

    edit: habe mir gerade noch ein paar andere Erklärungen durchgelesen.
    Ich glaube ich nutze eher das MVVM Modell.
    Aber das sollte ja auch i.O. sein

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

    Soho.
    Ich habe soweit die Grundfunktionen fertig. Ich habe es sogar hinbekommen, dass die erstellten UCs eine neue Location bekommen, wenn ich mittendrin eins lösche.
    Und ein - nach dem Löschen neu erstelltes UC - wird wieder am Ende eingefügt.
    Alles schick.

    Aber an einer Sache scheitere ich.
    Die UCs werden auf einem Panel dargestellt:

    VB.NET-Quellcode

    1. Private Sub CreateNewUC()
    2. _UCCount += 1
    3. Dim yPos As Integer = _UCCount * 61 + 10
    4. 'UC erstellen
    5. Dim newUC = New UCInvoice
    6. With newUC
    7. .DtsSettings = Me.DtsSettings
    8. .ArticleBindingSource.DataSource = Me.DtsSettings
    9. ._UCNumber = _UCCount
    10. .Location = New System.Drawing.Point(10, yPos)
    11. .Name = "TB " & _UCCount
    12. .AutoSize = True
    13. .Visible = True
    14. AddHandler newUC.TextBoxesChangedTheirContent, AddressOf UCChangeEvent
    15. AddHandler newUC.DellButtonPressed, AddressOf DeleteUC
    16. End With
    17. _UCList.Add(newUC)
    18. 'Control auf die Form malen
    19. Me.Panel2.Controls.Add(newUC)
    20. End Sub


    Ein UC soll sich aber über die komplette Bildschirmbreite erstrecken - unabhängig von der größe des Fensters der Hauptanwendung. Das schaffe ich jedoch nicht mal im Designer...
    Die einzige Möglichkeit die ich gefunden habe, ist Dockstyle auf .Fill zu setzen, dann füllt die UC zwar die gesamte Bildschirmbreite, aber wird eben mittig dargestellt.
    Habe ich hier Einstellungsmöglichkeiten, oder muss ich die Breite des Panels auslesen und entsprechend die Breite meines UCs setzen? Und muss ich die Breiten der UCs dann anpassen, wenn sich die Größe der Anwendung ändert?
    Bilder
    • Unbenannt.png

      51,85 kB, 1.920×1.080, 66 mal angesehen
    …behält aber die eingestellte Höhe, also wie gewünscht. Kommen weitere UCs mit DockStyle.Top hinzu, werden die unter die vorhandenen UCs drunter platziert.
    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.
    Hmm - also rein optisch funktioniert das ganze natürlich.

    Meine Codelogik geht jetzt aber davon aus, dass ein neues UC immer unter den vorhandenen eingefügt wird.
    Also es läuft ein Counter, der die Anzahl der vorhandenen UCs zählt und jede UC bekommt beim anlegen eine fortlaufende Nummer.
    Beim löschen eines UCs wird geprüft ob UCNummer = UCCounter. Wenn ja, werden nur die Properties der UC auf 0 gesetzt, aber das Control selber bleibt auf der Form (damit immer was zum Beschreiben da ist und ich keinen neue UC Erstellen Button brauche).
    Außerdem brauche ich den Counter, um nach dem Löschen einer UC aus der Mitte (also z.B. das dritte von 5) die verbleibenden UCs wieder korrekt anzuordnen, also damit zwischen 2 und 4 dann keine Lücke entsteht.
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Dim yPos As Integer
    2. For Each item As UCInvoice In _UCList
    3. yPos = item._UCNumber * 61 + 10
    4. item.Location = New System.Drawing.Point(10, yPos)
    5. Next


    Sehe ich richtig, dass 1. mit Dockstyle.Top immer dass zuerst erzeugte UC unten ist? - ich also beim Löschen nur prüfen muss, ob das UC die Nummer 1 hat, statt dass es das letzte ist?
    und dass ich mir 2. die Neuanordnung der UCs nach Löschen sparen kann, weil sie einfach immer "aneinanderkleben"?

    edit: Uh. Ich habe gerade mal eine Rechnung auf diese Weise erzeugt. Das geht garnicht. Ich komme nicht drauf klar, dass ein neuer Artikel oben eingefügt wird (könnte mich wohl dran gewöhnen, aber das gilt nicht für alle die mit dem Programm arbeiten werden sollen...)
    Gibt es eine andere Möglichkeit die UCs über die Formbreite zu erstrecken, oder eben diese unterhalb einer alten UC zu erzeugen?

    edit2: @mrMo Ich habe gerade einen kleinen Performance check gemacht.
    Dazu lasse ich in 50er Schritten gefüllte UCs auf die Form malen. Das Erstellen der UCs dauert (gezählt) 3 Sekunden - wobei ich in der Praxis immer nur eine Uc erstelle.
    sämtliche Berechnungen und alles weitere Funktioniert nach dem Erstellen - auch von 200 Ucs - ohne merkliche Verzögerung.
    Mein ProgrammierPc ist recht potent - i7 10. Gen - 16 GB RAm.
    Deswegen habe ich das ganze noch auf einem alten Acer R13 mit irgendeinem Pentium und 2GB Ram getestet. Hier dauert das Erstellen von den 50 UCs ca. 12 Sekunden, aber auch hier ist die restliche Performance ohne Probleme.

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