sauberer Programmaufbau (Klassen an Textfelder binden)

  • VB.NET

Es gibt 57 Antworten in diesem Thema. Der letzte Beitrag () ist von cl10k.

    sauberer Programmaufbau (Klassen an Textfelder binden)

    Hallo Forum,

    ich habe ein Form (unterteilt durch ein TabControl) auf dem sich zahlreiche Textboxen tummeln (100+). Zusätzlich habe ich mehrere (statische) Klassen, deren Properties ich nun an meine Textboxen (z.B. per Textchanged- oder Validated-Event) binden möchte.

    Es erscheint mir ein bisschen merkwürdig in Main_Form nun 100+ Textchanged/Validated-Subs für die einzelnen Controls reinzustopfen...

    Wie macht man es richtig? Wie strukturiert man diese Zuweisung eleganter?

    MfG
    Bittee nimm WPF.
    Mit WPF kannste die "Form" in beliebig viele ViewModel aufteilen welche automatisch und sauber via XAML an die Properties gebunden werden. Von den 100erten anderen Vorteilen von WPF mal abgesehen.


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
    Ou... Zeitdruck. Das ist natürlich schlecht. Wenn du der Meinung bist, dass du die Zeit nicht investieren kannst wirst du wohl bei WinForms bleiben müssen. Jedoch kann ich dir da so leid es mir tut auch nicht weiterhelfen. Wie gesagt einmal WPF immer WPF. Hab sowas nie wieder mit WinForms gemacht.


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
    Für eine Form mit 100 Textboxen würdest Du einen EventHandler für alle TextChanged Events der TextBoxen so anfügen ( am besten im Load Event der Form):

    VB.NET-Quellcode

    1. ...
    2. For Each c As Control In Me.Controls
    3. ' an alle textboxen mit bestimmten Namen einen EventHandler hängen
    4. If TypeOf (c) Is TextBox And c.Name.StartsWith("txtIrgendwas") Then
    5. AddHandler c.TextChanged, AddressOf textChangedHandler
    6. End If
    7. Next
    8. ....
    9. Private Sub textChangedHandler(ByVal sender As Object, ByVal e As EventArgs)
    10. ' hier Sender abfragen welche Textbox genau es war und casten
    11. End Sub


    Sollten die Textboxen nicht direkt auf der Form liegen , sondern in eingebetteten Containerelementen, so wirst Du die Control-Collections aller Control direkt durchlaufen müssen um die Textboxen zu finden. Geht relativ einfach über Rekursion.

    Dabei kannst Du gleich den Validated Handler mit Verweis auf eine andere Sub anfügen.

    noBlubb schrieb:

    funktioniert das auch mit CType(sender, TextBox)

    Du gewinnst die TextBox mit all ihre Original-Eigenschaft durch Casten des Sender auf eine Textbox zurück:

    VB.NET-Quellcode

    1. Private Sub textChangedHandler(ByVal sender As Object, ByVal e As EventArgs)
    2. ' hier Sender abfragen welche Textbox genau es war und casten
    3. Dim tBox As TextBox = CType(sender, TextBox) ' man kann direkt casten ohne Type-Abfrage, da der Addhandler nur Events von Textboxen hierher leitet
    4. End Sub


    Beispiel für die rekursive Function (ungetestet):

    VB.NET-Quellcode

    1. ' im Load event der Form
    2. getControls(Me) ' start mit der Form selber
    3. Private Sub getControls(ByVal c As Control)
    4. For Each containedControl As Control In c.Controls
    5. If TypeOf (containedControl) Is TextBox Then
    6. Debug.Print("Name der Textbox: {0}", containedControl.Name)
    7. End If
    8. Debug.Print(containedControl.Name)
    9. ' durchsuche container
    10. getControls(containedControl)
    11. Next
    12. End Sub
    Hallo Kangaroo,

    Danke für deine Antwort! Ich verstehe Sie nur nicht... Der vorgeschlagene Weg erscheint mir so unspezifisch.

    Ich habe für jede Textbox (gibt auch noch zig andere Controls) auch in irgendeiner Klasse exakt die passende Property. Zusätzlicher finden zwischen Property und Controls unterschiedlichste Arten der Validierung statt. Ich kann mir garnicht vorstellen, wie ich das so allgemeingültig formulieren soll, dass ich das wie in deinem Beispiel umsetzen könnte.

    Ich habe u.a. hier Beiträge gefunden die ein direktes Databinding zwischen Properties zulassen. Allerdings nur für nicht-statische Klassen...
    So richtig klar ist mir auch da nicht, wo ich meine Validierungen unterbringen sollte. Bisher habe ich es immer so gemacht:

    Textchanged Event
    - Format prüfen
    -weitere Bedingungen prüfen
    -Wenn ok - Daten an Klasseneigenschaft schicken

    Edit: Dieser Post bezieht sich auf deinen ersten Beitrag
    Mag sein dass meine Antwort leider nicht zu Deiner Frage passt ( war auch gerade zwischen Kinder zum x-ten Mal ins Bett zurückschicken und der wartenden Flasche Rotwein).

    Vielleicht könntest Du mal ein (einfaches) Beispiel formulieren ....
    Ich versuche nochmal mich etwas deutlicher auszudrücken :)

    Ich habe eine Klasse Zylinder, Properties sind Aussendurchmesser, Innendurchmesser und Höhe. Auf meinem Form gibt es entsprechend 3 Textboxen welche direkt den Properties zugeordnet werden können.

    Nun könnte ich in meinem Form die 3 TextchangedEvents der 3 Textboxen nehmen um den Inhalt der Textboxen an die 3 Properties zu binden (wir lassen Validierung mal außen vor).


    Also nach dem Muster:

    VB.NET-Quellcode

    1. Private Sub TxBx_Aussendurchmesser_TextChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TxBx_Aussendurchmesser.TextChanged
    2. 'hier Prüfbedingungen, wenn ok:
    3. Zylinder.Aussendurchmesser = TxBx_Aussendurchmesser.Text
    4. End Sub


    Nun habe ich aber nicht nur eine Klasse mit 3 Properties sondern tatsächlich zig Klassen mit zusammen 100+ Properties und die dazugehörigen Controls. Es erscheint mir so falsch jetzt 100+ TextchangedEvents für die einzelnen Controls in den Code von MainForm zu knallen. Das entbehrt irgendwie jeglicher Eleganz...

    Edit: Mir geht es erstmal nur ums Prinzip. Das TextChangedEvent wurde hier nur beispielhaft gewählt. Tatsächlich handelt es sich bei den Controls um UserControls, die z.B. einen Teil der Validierung bereits selber erledigen und noch ein paar spezielle Eigenschaften aufweisen...

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

    naja - mit Databinding wäre das alles sicherlich wesentlich eleganter gegangen.
    Guck dir mal in "DetailView" auf Movie-Tuts an, wie man mit einer Drag-Operation alle Properties eines Datensatzes gleichzeitig als datengebundene und beschriftete Textbox aufs Form generiert bekommt.
    Da schreibste keine Zeile Code für, und sowohl Text-Änderungen flutschen in den Datensatz, als auch Änderungen am Datensatz wiederspielgeln sich sofort in den Textboxen.
    Wie immer vielen Dank! Sehr witziges Tutorial!

    Der Ansatz bei so einer Menge an Controls und Properties ist also prinzpiell DataBinding?!

    Das geht (wohl) auch (siehe Link in meinem 2. 3. Beitrag) zwischen Klasseneigenschaften und Controls. Das werde ich mir dann mal zu Gemüte führen...

    Merci und Gute Nacht!
    also dein Link ist von 2004.
    Der kennt kein DatagridView, keine List(Of T), keine BindingList(Of T), keine BindingSource. Der wird noch mit ArrayList werkeln oder gar Collection(Of T) beerben (uff, uff!!).
    Kopier da lieber keinen Code raus.
    Such mal bisserl auf Forms over Data Videos - Beth Massi hat auch Videos dabei, bei denen eine Object-Database implementiert wird. Oder Object-Binding oder wie das hiess - war eine kleine Serie zum Thema.
    Ist schomal wesentlich eleganter, aber immer nochn Haufen Arbeit, da die ganze Funktionalität nu selbst dran-zu-erfinden, die in den Klassen des typDatasets schon ewig eingebaut ist.

    Übrigens auch Validierungs-Funktionalität ist in typisierten DataRows bereits angelegt, und braucht nur anwendungsbezogen erweitert zu werden.

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

    Nabend :)

    So, ich habe die letzten Tage damit verbracht, mir DataBinding anzuschauen. Man findet dazu sehr sehr viel wenn es darum geht reguläre Controls an beliebige Datenquellen zu binden, bei UserControls sieht es jedoch etwas mau aus. Zur Info: Die betreffenden Seiten zum Thema auf MSDN habe ich durch, ebenso die ersten x Seiten die google zum Thema ausspuckt...


    Nun zum Problem:

    Ich habe ein UserControl bestehend aus mehreren Labels und einer Textbox (TxBx_Input). Ein Property namens "Input" habe ich an die Text-Eigenschaft dieser Box "gebunden" und ein zugehöriges InputChanged Event erstellt.

    Auf das Wesentliche reduziert sieht das UserControl so aus:

    VB.NET-Quellcode

    1. Imports System.ComponentModel
    2. Public Class uc_TextBox
    3. 'EventInput
    4. Public Event InputChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)
    5. Private Sub _InputChanged(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles TxBx_Input.TextChanged
    6. RaiseEvent InputChanged(sender, e)
    7. MsgBox("InputChanged") 'nur zu Testzwecken
    8. End Sub
    9. 'Input
    10. Public Property Input() As Object 'Object weil die TextBox Integer, Single und Strings aufnehmen können soll (das wird auch validiert, den Teil habe ich aber hier weggelassen...)
    11. Get
    12. Return TxBx_Input.Text
    13. End Get
    14. Set(ByVal value As Object)
    15. TxBx_Input.Text = CStr(value)
    16. End Set
    17. End Property
    18. End Class

    In meiner Anwendung taucht eine Instanz dieses UserControls als TxBx_Hull_Outerdiameter auf...




    Dann habe ich eine Klasse erstellt (wieder nur das Wesentliche):

    VB.NET-Quellcode

    1. Imports System.ComponentModel
    2. Public Class cls_Hull
    3. #Region "BiDirectional Databinding"
    4. Implements INotifyPropertyChanged
    5. Public Event PropertyChanged(ByVal sender As Object, ByVal e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
    6. Private Sub NotifyPropertyChanged(ByVal info As String)
    7. RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(info))
    8. End Sub
    9. #End Region
    10. 'Aussendurchmesser
    11. Private _OuterDiameter As Single
    12. Public Property OuterDiameter() As Single
    13. Get
    14. Return _OuterDiameter
    15. End Get
    16. Set(ByVal value As Single)
    17. _OuterDiameter = value
    18. NotifyPropertyChanged("OuterDiameter")
    19. End Set
    20. End Property
    21. End Class



    Bei Programmstart wird Hull als neue Instanz der Klasse cls_Hull angelegt und im frm_Main Load-Event ein DataBinding festgelegt:

    VB.NET-Quellcode

    1. Me.TxBx_Hull_OuterDiameter.DataBindings.Add("Input", frm_Main.Hull, "OuterDiameter", True, DataSourceUpdateMode.OnPropertyChanged)


    Aber es funktioniert nicht! :cursing:

    Gebe ich Werte in meine Textbox ein, werden diese nicht an meine Klasseneigenschaft Hull.OuterDiameter weitergereicht. Wenn ich diese Eigenschaft allerdings per Code ändere (z.B. per Button: Hull.Diameter=666) ändert sich ohne zu murren auch der Inhalt des UserControls.

    Ich bastle jetzt seit ein paar Tagen daran herum, aber alle meine Versuche sind bisher kläglich gescheitert. Wenn ich mich nicht täusche, sollte es etwas mit dem InputChanged-Event im UserControl zu tun haben, aber ich kriegs nicht hin.

    Jeder Tipp wird dankend angenommen...

    lg

    Edit: Mit einer regulären TextBox und einem Binding an die Text-Property funktioniert es tadellos in beide Richtungen...

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

    probierma
    1. Input als <DataBindable(True)> attributieren.
    2. als String zu deklarieren - bei Textbox.Text klappt das ja auch.
    3. Textbox.Text im IlSpy dekompilieren und angugge, wie MS das gemacht hat.


    Ansonsten habich bei UserControls gelegentlich eine Art "PseudoDatabindability" implementiert, nämlich einfach eine Property vom Typ BindingSource, und im Designer kann man dann eine BindingSource auswählen, die dem ucl dann zugewiesen wird.
    Alle weiteren Bindings dann innerhalb des ucls an diese von extern zugewiesene BindingSource.
    Hallo EdR,

    du bist auch wirklich eine Konstante hier im Forum... Danke!


    Die ersten zwei Vorschläge führen nicht zum Ziel (auch nicht in Kombination).

    Die anderen Ideen schaue ich mir morgen an.

    lg

    PS: ilspy ist ja verdammt cool...

    PPS: Das Einzige was zum Kontext passen würde, ist "BindsTwoWayByDefault" MSDN. Eigentlich wollte ich ja Schlafen gehen... :thumbsup:

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

    Um mal was anderes zu hören:
    Du kannst die Controls jedes Tabs einzeln in je ein User-Control packen und dann das TabControl mit diesen UserControls bestücken.
    Da haste die Tab-Funktionalität in je einer UserControl-Klasse gekapselt.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    RodFromGermany schrieb:

    Um mal was anderes zu hören:



    Guten Morgen Rod, ich bin dankbar für jeden Kommentar (von jedem User) :) !

    RodFromGermany schrieb:


    Du kannst die Controls jedes Tabs einzeln in je ein User-Control packen und dann das TabControl mit diesen UserControls bestücken.
    Da haste die Tab-Funktionalität in je einer UserControl-Klasse gekapselt.


    Welchen Vorteil habe ich davon?

    cl10k schrieb:

    Welchen Vorteil habe ich davon?
    aufgeräumten Code, Kapselung des Codes entsprechend der Strukturierung der Tabs, bessere Wartbearkeit, ...
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!