Announcement

Collapse
No announcement yet.

Datenbindung und Abrechen der Eingabe

Collapse
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Datenbindung und Abrechen der Eingabe

    Hallo,

    ich beginne gerade, mich etwas mit der Datenbindung in VB.NET vertraut zu machen und stolpere gleich über die einfachsten Sachen. Ich hoffe, VB.NET hat dafür auch eine Lösung, sonst kann man sich die ganze Datenbindung sparen. Hier nun das Problem:

    Der Einfachheit halber sagen wir, ich hätte eine Klasse mit nur einer Eigenschaft namens "TextProperty". Diese ist vom Typ "String".

    Code:
    Public Class TestClass
      Private textProperty As String
    
      Public Property TextProperty As String
        Get
          Return textProperty
        End Get
        Set(ByVal value As String)
          textProperty = value
        End Set
      End Property
    End Class
    Des Weiteren habe ich ein WinForms-Formular mit einer Textbox "Textbox1" sowie einer Abbrechen- und einer OK-Schaltfläche.

    Diesem Formular wird im Konstruktor ein TestClass-Objekt übergeben und dessen "TextProperty"-Eigenschaft an die "Text"-Eigenschaft der Textbox gebunden.

    Code:
    Me.Textbox1.DataBinding.Add("Text", testClass, "TextProperty")
    Öffne ich jetzt das Formular und ändere den Text in der Textbox, so wird dieser geänderte Text auch brav in die gebundene TextProperty-Eigenschaft übertragen.

    Wie kann man es jedoch bewerkstelligen, dass beim Betätigen von "Abbrechen" diese Änderung eben NICHT weitergegeben wird. Bei mehreren gebundenen Steuerelementen müsste sie ja quasi sogar rückgängig gemacht werden.

    Wenn ich mich selbst um das Zwischenspeichern der alten Werte und das Wiederherstellen des Urzustands kümmern muss, dann ist die ganze Datenbindung für die Katz.

    Aber vielleicht übersehe ich hier ja was.
    Vielen Dank im Voraus für den Wink mit dem Zaunpfahl.

  • #2
    Ich denke so einfach geht das nicht. Rückgängig machen lässt sich eine Änderung vor dem Verlassen der Textbox, wenn man z.B. auf escape drückt. Sobald aber die TextBox den Fokus verliert wird die Validierung ausgeführt und sobald diese erfolgreich ist wird der Wert in die Property übernommen.
    Du könntest Deine Klassen als IClonable definieren. Wenn diese an das Formular übergeben werden, dann clonst Du Dir die Klasse. Beim speichern übernimmst Du die Änderungen der geklonten Klasse. Bei Abbrechenn werden die Änderungen verworfen. Das wäre jetzt was was mir auf die schnelle einfiele.

    Comment


    • #3
      Wie hast du die Datenhaltung hinter deiner UI geregelt? Deine Notwendigkeit eines Undo an der UI beim Abbrechen hört sich nach einem Designfehler im Hintergrund an.

      Üblicherweise hat man ja zumindest 2 Schichten in der Anwendung eine zur Visualisierung und eine zur Datenhaltung. Wenn du also ein Objekt von der Datenhaltung and die Visualisierung gegeben hast sollte beim Abbrechen in der UI ein Verwerfen dieses Objekts ausreichen, außer du benötigst mehrstufiges Undo/Redo nur dann müßte man etwas aufwendiger rangehen. In einem klassischen Übernehmen/Abbrechen System sollte der Abbrechen Fall aber eigentlich simpel sein.

      Comment


      • #4
        Eine Datenbindung übernimmt, wie der Name schon sagt, die Bindung der Daten (an ein Control). In ihnen steckt keine Logik, was mit den Daten passieren soll (Abbruch, Speichern). Daher ersparen sie lediglich die ohne Datenbindung üblichen Prozeduren wie Form2Data() bzw. Data2Form().
        Benötigt man "einen doppelten Boden" für die Datenhaltung, dann sollten Klassen verwendet werden, die das von Haus aus können. Instanzen der Klasse System.Data.DataTable sind in der Lage, zwei Versionen der Daten vorzuhalten. Mit dem Aufruf von AcceptChanges() werden die aktuellen Werte festgeschrieben (entspricht Commit in einer Datenbank), RejectChanges() verwirft mögliche Änderungen und stellt den Zustand nach dem letzten AcceptChanges() wieder her (Rollback in einer Datenbank).
        Sollte aus programmtechnischer Sicht etwas gegen die Verwendung von einer DataTable sprechen, ist die Datenversionierung selbst zu übernehmen. Dafür sollten geeignete Strukturen implementiert werden, so daß die Versionierung nur einer einzigen Klasse überlassen bleibt, man sich selbst nur um das Schalten der Datenversion kümmern muß (Commit oder Rollback).
        Nachfolgend ist ein Lösungsweg skizziert, wie die Datenversionierung implementiert werden kann. Zunächst gibt es die Schnittstelle IDataOwner, die die zwei Events CommitRequest und RollbackRequest definiert. Trifft ein solches Event ein, rufen die dieses Event verarbeitenden Klassen ihrerseits ihre Commit() bzw. Rollback() Routine auf. Die generische Klasse VersionedData(Of T) kann zwei Versionen der Daten in Value vorhalten und hat Handler für CommitRequest und RollbackRequest definiert, die entweder den aktuellen Datenbestand festschreiben oder verwerfen.

        Angewendet auf das vorgestellte Szenario (Form1, TestClass) implementiert die Form1 wie auch die TestClass die Schnittstelle IDataOwner. Ein Commit() bzw. Rollback() wird via Event dem Gesamtsystem bekanntgemacht, ohne daß die Form1-Instanz "wissen" muß, welche Daten versioniert werden können.

        Form1.vb
        [highlight=vbnet]Public Class Form1
        Implements IDataOwner

        Private _testClass As New TestClass(Me)
        Public ReadOnly Property TestClass() As TestClass
        Get
        Return _testClass
        End Get
        End Property

        ''' <summary>
        ''' Daten festschreiben
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub Commit() Implements IDataOwner.Commit
        RaiseEvent CommitRequest(Me, New EventArgs)
        End Sub
        Private Event CommitRequest(ByVal sender As Object, ByVal e As System.EventArgs) Implements IDataOwner.CommitRequest
        ''' <summary>
        ''' Änderungen verwerfen
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub Rollback() Implements IDataOwner.Rollback
        RaiseEvent RollbackRequest(Me, New EventArgs)
        End Sub
        Private Event RollbackRequest(ByVal sender As Object, ByVal e As System.EventArgs) Implements IDataOwner.RollbackRequest
        End Class[/highlight]

        IDataOwner.vb
        [highlight=vbnet]Public Interface IDataOwner
        Sub Commit()
        Sub Rollback()

        Event CommitRequest(ByVal sender As Object, ByVal e As EventArgs)
        Event RollbackRequest(ByVal sender As Object, ByVal e As EventArgs)
        End Interface[/highlight]

        VersionedData.vb
        [highlight=vbnet]Public Class VersionedData(Of T)

        #Region "Commit"
        Private Sub _dataOwner_CommitRequest(ByVal sender As Object, ByVal e As System.EventArgs) Handles _dataOwner.CommitRequest
        Commit()
        End Sub

        ''' <summary>
        ''' Schreibt Änderungen fest (alter Originalwert wird überschrieben)
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub Commit()
        'Daten festschreiben
        If _valuePointer = 1 Then
        _valuePointer = 0
        _value(0) = _value(1)
        _value(1) = Nothing
        End If
        End Sub
        #End Region

        #Region "Rollback"
        Private Sub _dataOwner_RollbackRequest(ByVal sender As Object, ByVal e As System.EventArgs) Handles _dataOwner.RollbackRequest
        Rollback()
        End Sub

        ''' <summary>
        ''' verwirft Änderungen
        ''' </summary>
        ''' <remarks></remarks>
        Public Sub Rollback()
        'Daten verwerfen
        _valuePointer = 0
        _value(1) = Nothing
        End Sub
        #End Region

        Public Sub New()
        End Sub
        Public Sub New(ByVal dataOwner As IDataOwner)
        _dataOwner = dataOwner
        End Sub

        Private WithEvents _dataOwner As IDataOwner
        Public Property DataOwner() As IDataOwner
        Get
        Return _dataOwner
        End Get
        Set(ByVal value As IDataOwner)
        _dataOwner = value
        End Set
        End Property

        ''' <summary>
        ''' zeigt vorliegende Änderungen an
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks>
        ''' </remarks>
        Public ReadOnly Property HasChanges() As Boolean
        Get
        Return (_valuePointer = 1)
        End Get
        End Property

        Private _valuePointer As Integer = -1
        Private _value As T() = Array.CreateInstance(GetType(T), 2)
        ''' <summary>
        ''' Wert
        ''' </summary>
        ''' <value></value>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Property Value() As T
        Get
        If _valuePointer < 0 Then
        Return Nothing
        Else
        Return _value(_valuePointer)
        End If
        End Get
        Set(ByVal value As T)
        'Verbesserungsvorschlag: an Position 1 nur dann Werte schreiben,
        'wenn sie sich vom Wert an Position 0 unterscheiden
        If _valuePointer < 1 Then _valuePointer += 1
        _value(_valuePointer) = value
        End Set
        End Property

        End Class[/highlight]

        Comment


        • #5
          Dann gebe ich hier mal auch meinen Senf mit einer einfacheren Lösung dazu

          Wenn Du einen Rollback/Undo benötigst, solltest Du die Eingabefelder nicht an ein "lebendes" Objekt binden, sondern an ein eigenes Objekt, das erst auf einen Commit hin die Daten an das lebende Objekt überträgt.

          [highlight=vbnet]
          Public Class myForm

          Private myData as clsFormData ' das Objekt an das die Eingabefelder gebunden sind
          Private origData as clsFormData ' Verweis auf Urform der Daten für Rückgabe

          Public Sub New(ByRef DataToEdit as clsFormData)
          ...
          myData = DataToEdit.Clone()
          origData = DataToEdit
          ...
          End Sub

          Private Sub CommitForm()
          myData.TransferTo(origData)
          End Sub
          End Class
          [/highlight]

          Ich weiß jetzt nicht, ob das syntaktisch so korrekt ist mit dem Verweis, das Beispiel soll ja auch nur den eigentlichen Weg zeigen

          Dies ist quasi ein etwas einfacherer Weg, als den Rollback in dem Datenobjekt selbst zu handeln. Dadurch, dass dann solche Übertragungsroutinen wie clsFormData.TransferTo(clsFormData) notwendig sind, ist die Notwendigkeit/Sinnhaftigkeit einer Datenbindung allerdings in Frage gestellt, denn ob man die Daten von einem Form in ein Objekt oder von einem Objekt in das andere überträgt, ist im Grunde das gleiche, man hat nur gewonnen, dass eine Gültigkeitsprüfung von dem Objekt selbst durchgeführt werden kann, und nicht im Form implementiert wird.

          Gruß
          Martin Dietz

          Comment

          Working...
          X