Winforms Data Binding: Auf die Quelle kommt es an

Gestern habe ich mich ein paar Stunden mit WinForms Data Binding auseinandersetzen müssen. Puh, keine so leichte Sache. Microsoft hat es bestimmt gut gemeint dabei - aber ob wirklich am Ende etwas rundum Gutes herausgekommen ist? Hm... Ich weiß nicht, ich weiß nicht. Es funktioniert zwar auf weite Strecken ganz nett, aber so richtig reibungslos ist es in nicht trivialen Szenarien nicht. Trotzdem habe ich mich damit durchaus angefreundet und es erleichtert die Arbeit gerade beim Prototyping.

Eine meiner Erkenntnisse ist z.B.: Übersichts- und Bearbeitungsfenster müssen an dieselbe Datenquelle gebunden werden, wenn Data Binding Spaß machen soll.

Ok, was meine ich mit "Übersichts- und Bearbeitungsfenster"? Ein typisches Szenario bei der Datenbankprogrammierung ist, eine Liste von Datensätzen in einem Fenster anzuzeigen (Übersicht) und den Anwender zur Bearbeitung daraus einen Datensatz auswählen zu lassen.

Die Bearbeitung findet dann in einem eigenen Fenster (Bearbeitung) statt. Die Datenbindung im Übersichtsfenster ist einfach und wie zu erwarten:

Private _ds as DataSet
Private _dv as DataView
...

Dim conn As New SqlConnection("...;database=northwind")
Dim adap As New SqlDataAdapter("select ..., companyname from customers", conn)

_ds = New DataSet
adap.Fill(_ds, "customers")

_dv = New DataView(_ds.Tables("customers"))
DataGrid1.DataSource = _dv

Daten laden, Daten binden. Fertig. Allerdings: Gebunden werden sollte immer ein expliziter DataView. Man kann zwar auch eine DataTable oder ein DataSet binden, aber meine Empfehlung ist, einen eigenen DataView zu erzeugen.

Damit hat man die Möglichkeit, z.B. jederzeit die Bearbeitung der Übersicht einzuschränken, z.B.

_dv.AllowEdit = false

Außerdem hat man damit ein Objekt in der Hand, das auch bei weiteren Bindungen genutzt werden kann. Wenn man z.B. Daten der Tabelle im Übersichtsfenster auch nochmal in einer Textbox anzeigen möchte, dann ist das kein Problem:

TextBox1.DataBindings.Add("text", _dv, "companyname")

Entscheidend ist, dabei auch wieder _dv als Quelle anzugeben, um die Zahl der CurrencyManager zu minimieren. Für DataGrid1 und TextBox1 wäre in diesem Fall derselbe CurrencyManager zuständig, d.h. wenn man im Grid weiterblättert, ändert sich auch die Anzeige in der Textbox.

Soweit so gut. Das hatte ich gestern im Grunde auch gemacht. Ein Problem trat aber auf, als ich dann ein Bearbeitungsfenster öffnen wollte. Was habe ich da so ganz naiv gemacht? Im Übersichtsfenster habe ich den zu bearbeitenden Satz ermittelt und ihn an das Bearbeitungsfenster übergeben:

Dim frmBearb As New Bearbeitungsfenster()
Dim bm As BindingManagerBase = Me.BindingContext(_dv)
Dim rv As DataRowView = bm.Current
frmBearb.ShowDlg(rv)

Das schien mir völlig plausibel. Warum sollte das Bearbeitungsfenster mehr kennen, als den zu bearbeitenden Satz? Im Bearbeitungsfenster habe ich dann wie folgt gebunden:

Private _rv as DataRowView

Public Sub ShowDlg(ByVal rv As DataRowView)
    TextBox1.DataBindings.Add("text", rv, "companyname")
    _rv = rv
    Me.ShowDialog()
End Sub

Auch kein Problem. Und alles wäre gut geblieben, wenn ich denn den Inhalt der anzuzeigenden Spalte companyname nur durch Eingabe in die Textbox im Bearbeitungsfenster hätte verändern wollen.

Ich wollte aber etwas anderes! Einige gebundene Spalten sollten durch Code verändert werden, z.B.

_rv("companyname") = "..."

Das funktioniert auch, man sieht die Änderung in der Tabelle des Übersichtsfensters - aber sie erscheint nicht (!) in der Textbox des Bearbeitungsfenster. Warum das so ist, ist mir nicht wirklich klar.

Dafür habe ich aber einen Workaround gefunden, den ich unterm Strich ohnehin für den konsequenteren Weg halte. Das Übersichtsfenster übergibt nicht mehr die in Bearbeitungs befindliche Tabellenzeile, sondern die Datenquelle, an die das Grid bebunden ist inkl. der Position, auf der es gerade steht:

Dim frmBearb As New Bearbeitungsfenster()
Dim bm As BindingManagerBase = Me.BindingContext(_dv)
frmBearb.ShowDlg(_dv, bm.position)

Und das Bearbeitungsfenster bindet an dieselbe Datenquelle wie das Übersichtsfenster:

Public Sub ShowDlg(ByVal dv as DataView, index as Integer)
    TextBox1.DataBindings.Add("text", dv, "companyname")
    me.BindingContext(dv).Position = index
    _rv = me.BindingContext(dv).Current
    Me.ShowDialog()
End Sub

Damit dann aber auch dort der richtige Satz angezeigt wird, muss noch der CurrencyManager auf den selben Satz wie das Übersichtsfenster gestellt werden.

Das mag etwas weniger intuitiv sein, als den zu bearbeitenden Satz zu übergeben, aber der Effekt ist der selbe und dieser Weg bietet mehr Freiheiten. Jetzt ist die Veränderung der angezeigten Daten auch über eine Zuweisung an den DataRowView möglich.

Fazit: Beim Data Binding sollte man sorgfältig darauf achten, dass alle Controls, die grundsätzlich an der selben Datenquelle hängen, auch wirklich über das selbe Objekt (am besten bzw. in den meisten Fällen ein DataView) gebunden werden.

1 Comment

Comments have been disabled for this content.