Debugging von WPF Data- Bindings
Schon öfters wurde ich bezüglich eines nicht richtig funktionierenden WPF Data-Binding zur Hilfe gebeten. Ebenfalls tauchen auch immer wieder auf verschiedenen Foren wie Stackoverflow.com Fragen dazu auf.
Hier ein paar Tipps zum Debuggen von Data-Binding Fehlern:
TraceLevel erhöhen
Normalerweise erscheinen im Debug Output Window nur sehr wenige Infos bezüglich Data-Bindings. Um detaillierte Informationen (z.B. auch Value-Änderungeng bei einem funktionierenden Binding) zu erhalten kann man ganz einfach den TraceLevel auf ‘High’setzen.
Beispiel:
<TextBox Text=“{Binding Path=TextA, PresentationTraceSources.TraceLevel=High}”/>
Die meisten Binding-Fehler werden damit sehr schnell ersichtlich.
Ps.: Nicht vergessen das TraceLevel- Property wieder zu löschen. Erstens werden sonst später Unmengen unnötiger Outputs generiert und zweitens wird der Debugger merklich langsamer.
Falsch initialisierte Dependency Properties
Dieser Error bezieht sich zwar nicht direkt auf Data-Bindings, führt aber sehr oft zu nicht funktionierenden Bindings. Ein Auge sollte man auf die Registrierung des DependencyProperties werfen.
Beispiel:
internal class TestClass
{
public static readonly DependencyProperty SomeStringProperty = DependencyProperty.Register(“SomeString”, typeof (string), typeof (TestClass), new UIPropertyMetadata(null));
}
Als dritten Parameter der oben beschriebenen Register Methode muss der OwnerType übergeben werden. Mit wenigen Ausnahmen ist dies wie der Name schon sagt der Typ in welchem das Property definiert wurde (wie hier TestClass).
In Folge von Flüchtigkeitsfehlern (Copy-Paste, Verschieben des Dependency Properties von einer Klasse in eine andere) passiert es nun leider öfters, dass der OwnerType falsch gesetzt ist. Oftmals tritt erst zur Laufzeit ein Problem auf und die geworfenen Exceptions deuten nicht immer auf das wirkliche Problem hin.
Ps.: Visual Studio oder Programme wie ReSharper geben keine Warning aus.<!> Ich hab mir ein kleines Programm geschrieben, welches alle Klassen nach Dependency Properties durchforstet die den falschen “OwnerType” haben.
Binding Chains
Als letztes noch einen Tipp zu einem Problem welches zwar eher selten auftritt, jedoch auch ganz hilfreich für das Verständnis bezüglich Data-Bindings ist.
Nehmen wir an, wir haben ein TextBox-Control als Eingabefeld (tb1).
XAML:
<TextBox x:Name=“tb1″/>
Nun möchten wir den Value des TextBox.Text- Properties mit zwei weiteren Properties über Data-Binding verbinden. Die Bindings sollen aber nicht im XAML gemacht werden sondern mittels Code:
internal class TestClass
{
public static readonly DependencyProperty StringAProperty = DependencyProperty.Register(“StringA”, typeof (string), typeof (TestClass), new UIPropertyMetadata(null));
public static readonly DependencyProperty StringBProperty = DependencyProperty.Register(“StringB”, typeof (string), typeof (TestClass), new UIPropertyMetadata(null));
public TestClass()
{
// Binding für das erste Property:
var binding1 = new Binding(“StringA”) { Source = this, Mode = BindingMode.OneWay };
tb1.SetBinding(TextBox.TextProperty, binding1);
// Binding für das zweite Property:
var binding2 = new Binding(“StringB”) { Source = this, Mode = BindingMode.OneWay };
tb1.SetBinding(TextBox.TextProperty, binding2);
}
}
Wenn wir das Beispiel nun laufen lassen, stellen wir fest, dass die Bindings nicht funktionieren. Aber warum?
Grundsätzlich kann einem Dependency Property nur ein Data-Binding attached werden. Oben im Code wird aber dem TextProperty der TextBox tb1 zweimal ein Binding gesetzt. Ein SetBinding() löscht aber als erstes immer das schon bestehende Binding.
Deklariert man die Bindings stattdessen im XAML ist es offensichtlich, dass nicht zwei Bindings gesetzt werden können:
<!–Es kann nur ein Property für das Binding verwendet werden–>
<TextBox x:Name=”tb1″ Text=“{Binding StringA}“/>
Lösung:
In diesem speziellen Beispiel könnte man natürlich ein MultiBinding verwenden. Als alternativen Weg könnte man nun aber das eine Binding ‘verdrehen’ (was zu einer sogenannten ‘Binding Chain’ führt):
// Binding für das erste Property:
var binding1 = new Binding(“StringA”) { Source = this, Mode = BindingMode.OneWay };
tb1.SetBinding(TextBox.TextProperty, binding1);
// Binding für das zweite Property:
var binding2 = new Binding(“Text”) { Source = tb1, Mode = BindingMode.OneWay };
SetBinding(TestClass.StringBProperty, binding2);
Somit haben wir jetzt nur noch je ein Binding ‘attached’.
Ps.: Zu beachten gilt es hier, dass dieser Weg nur funktioniert, wenn die Properties (hier StringPropertyA und StringPropertyB) Dependency Properties sind, da dies für Bindings zwingend erforderlich ist.