Jeder kann coden / Programmieren & TicTacToe / Objektorientierte Programmierung
Data Binding mit WPF und C#¶
Databinding in C# und WPF (Windows Presentation Foundation) ist eine leistungsstarke Technik, um Benutzerschnittstellen (UI) mit Datenquellen zu verbinden. Es ermöglicht die automatische Synchronisation zwischen dem UI und dem dahinterliegenden Datenmodell. Lass uns das Thema strukturiert und detailliert aufarbeiten:
Was ist Data Binding in WPF?¶
Data Binding bedeutet in WPF, dass du Eigenschaften von UI-Elementen (wie TextBox
, Label
, ComboBox
, etc.) mit Datenquellen (z. B. Objekten, Listen oder ViewModels) verknüpfst, sodass Änderungen in der einen Komponente automatisch in der anderen reflektiert werden – und umgekehrt, wenn gewünscht.
Beispiel (einfaches One-Way Binding):¶
xaml
<TextBlock Text="{Binding Name}" />
Hier erwartet WPF, dass im DataContext des UI-Elements ein Objekt mit einer Name
-Eigenschaft existiert.
Grundlagen des DataBindings¶
WPF unterstützt mehrere Binding-Modi:
Binding Mode | Beschreibung |
---|---|
OneWay |
Von Datenquelle zur UI (Standard für viele Controls). |
TwoWay |
Datenquelle ↔ UI – Änderungen wirken in beide Richtungen (z. B. bei TextBox ). |
OneWayToSource |
Nur von UI zur Datenquelle. |
OneTime |
Nur ein einmaliges Binding beim Laden der UI. |
Default |
Das Control entscheidet selbst. |
Beispiel für TwoWay
Binding:¶
xaml
<TextBox Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Ohne
UpdateSourceTrigger=PropertyChanged
wird die Datenquelle erst beim Verlassen des Felds aktualisiert.
Warum funktioniert Binding manchmal nicht?¶
Einige UI-Elemente unterstützen kein Binding auf bestimmte Eigenschaften – oder es sieht so aus, als ob es nicht funktioniert.
Gründe:¶
Keine DependencyProperty: Nur Eigenschaften, die als
DependencyProperty
implementiert sind, können gebunden werden.🔧 Lösung: Du kannst eigene DependencyProperties definieren:
public static readonly DependencyProperty MyValueProperty = DependencyProperty.Register("MyValue", typeof(string), typeof(MyControl)); public string MyValue { get { return (string)GetValue(MyValueProperty); } set { SetValue(MyValueProperty, value); } }
Fehlender DataContext: Wenn der DataContext nicht gesetzt ist, kann das Binding nicht aufgelöst werden.
🔧 Lösung: Setze den DataContext manuell:
this.DataContext = new MyViewModel();
oder in XAML:
xaml <Window.DataContext> <local:MyViewModel /> </Window.DataContext>
Binding-Fehler durch falschen Pfad oder fehlende Properties: Tippfehler im Binding-Pfad, z. B.
Nam
stattName
, führen zu einem stillen Fehler.🔎 Debugging-Tipp: In der Output-Konsole siehst du Binding-Fehler während der Laufzeit.
Bidirektionales Binding einrichten (TwoWay Binding)¶
Damit bidirektionales Binding funktioniert, müssen drei Voraussetzungen erfüllt sein:
- Die gebundene Eigenschaft im ViewModel muss schreibbar sein (also ein Setter besitzen).
- Das UI-Element muss TwoWay-Binding unterstützen (z. B.
TextBox.Text
,CheckBox.IsChecked
, etc.). - Das ViewModel muss das Interface
INotifyPropertyChanged
implementieren, damit Änderungen reflektiert werden.
Beispiel ViewModel:¶
public class PersonViewModel : INotifyPropertyChanged
{
private string name;
public string Name
{
get => name;
set
{
if (name != value)
{
name = value;
OnPropertyChanged(nameof(Name));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
XAML (bidirektionales Binding):¶
xaml
<TextBox Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Zusammenfassung – Wann funktioniert DataBinding:¶
Voraussetzung | Beschreibung |
---|---|
DependencyProperty |
Das Ziel des Bindings (z. B. Text ) muss eine DependencyProperty sein. |
INotifyPropertyChanged |
Die Quelle (z. B. ViewModel) muss Änderungen melden. |
Richtiger DataContext |
Ohne DataContext kein Binding. |
Richtiger BindingMode |
Für TwoWay explizit angeben (meistens). |
Binding-Pfad korrekt | Property muss genau so heißen wie angegeben. |
Bonus: Debugging von Bindings¶
Du kannst dir fehlschlagende Bindings während der Laufzeit ansehen, indem du im Output-Fenster von Visual Studio nach "BindingExpression
" suchst.
Vertiefung¶
UpdateSourceTrigger – wann wird das Binding aktualisiert?¶
Der UpdateSourceTrigger
bestimmt, wann die Änderung von der UI zurück in die Datenquelle geschrieben wird:
Trigger | Beschreibung |
---|---|
Default |
Je nach Control unterschiedlich (z. B. LostFocus für TextBox ). |
PropertyChanged |
Bei jeder Zeichenänderung sofort in Datenquelle. |
LostFocus |
Erst wenn Control den Fokus verliert. |
Explicit |
Nur wenn manuell mit BindingExpression.UpdateSource() ausgelöst. |
💡 Tipp: Für TextBox
ist UpdateSourceTrigger=PropertyChanged
oft angenehmer in MVVM-Szenarien.
Binding an verschachtelte Objekte / komplexe Pfade¶
Du kannst an verschachtelte Properties binden:
xaml
<TextBlock Text="{Binding Address.City}" />
Aber Vorsicht: Wenn Address
null
ist, bricht das Binding. Um das zu vermeiden:
xaml
<TextBlock Text="{Binding Address.City, TargetNullValue='n/a'}" />
Oder mit FALLBACK
:
xaml
<TextBlock Text="{Binding Address.City, FallbackValue='(unbekannt)'}" />
ObservableCollection – für Listen mit automatischer Aktualisierung¶
Wenn du eine Liste von Elementen bindest, z. B. in einer ListView
:
public ObservableCollection<Person> People { get; } = new ObservableCollection<Person>();
→ ObservableCollection
informiert die UI automatisch über Hinzufügen/Entfernen von Einträgen.
Converter – wenn der Typ nicht direkt passt¶
Du kannst einen Wertkonverter einsetzen, um Daten beim Binden umzuwandeln:
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) =>
(bool)value ? Visibility.Visible : Visibility.Collapsed;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) =>
(Visibility)value == Visibility.Visible;
}
xaml
<Window.Resources>
<local:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
</Window.Resources>
<TextBlock Visibility="{Binding IsVisible, Converter={StaticResource BoolToVisibilityConverter}}" />
Validierung mit Data Binding¶
Du kannst Validierungen im Binding integrieren – z. B. über IDataErrorInfo
oder INotifyDataErrorInfo
.
Außerdem kannst du Fehler visuell anzeigen lassen:
xaml
<TextBox Text="{Binding Name, ValidatesOnDataErrors=True, NotifyOnValidationError=True}" />
RelativeSource, ElementName & StaticResource Binding¶
Nicht nur der DataContext
ist eine mögliche Quelle:
Binding-Typ | Beispiel | Zweck |
---|---|---|
ElementName |
{Binding Path=Text, ElementName=myTextBox} |
Bindet an anderes Element. |
RelativeSource |
{Binding Path=DataContext.SomeProp, RelativeSource={RelativeSource AncestorType=Window}} |
Bindet an Vorfahren. |
StaticResource |
{Binding Source={StaticResource myObject}, Path=SomeProperty} |
Bindet an ein Objekt aus den Ressourcen. |
Custom Controls und Bindings¶
Wenn du eigene Steuerelemente entwickelst und möchtest, dass sie Bindings unterstützen, musst du DependencyProperties verwenden (siehe oben). Normale .NET-Properties funktionieren nicht mit dem WPF-Binding-System.
Binding an Commands¶
Ein elementarer Bestandteil von MVVM ist das Command-Binding:
xaml
<Button Command="{Binding SaveCommand}" Content="Speichern" />
Das Property im ViewModel:
public ICommand SaveCommand { get; }
Mit z. B. RelayCommand
oder DelegateCommand
.
Best Practices¶
- 🔄 Immer
INotifyPropertyChanged
verwenden – oderObservableObject
von MVVM-Frameworks wie CommunityToolkit.Mvvm. - 🧼 Fehlerhafte Bindings nicht ignorieren – nutze Debug-Ausgabe.
- 🧪 Schreibe Tests für ViewModels unabhängig vom UI.
- 🏗 Verwende MVVM-Struktur für klare Trennung von Logik und Darstellung.
Beispiel¶
Wir können das Beispiel aus den WPF-Grundlagen so erweitern, dass es MVVM und DataBinding verwendet – also Model-View-ViewModel. Dabei bauen wir:
- Ein ViewModel, das den Zustand des Spielfelds hält.
- Eine ObservableCollection, um das Spielfeld zu rendern.
- Bidirektionales Binding zwischen View und ViewModel.
Schritt-für-Schritt: TicTacToe mit MVVM und DataBinding¶
1. Model: Zelle des Spielfelds¶
public class Cell : INotifyPropertyChanged
{
private string content;
public string Content
{
get => content;
set
{
if (content != value)
{
content = value;
OnPropertyChanged(nameof(Content));
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
2. ViewModel¶
public class GameViewModel : INotifyPropertyChanged
{
public ObservableCollection<Cell> Board { get; }
private string currentPlayer = "X";
public GameViewModel()
{
Board = new ObservableCollection<Cell>();
for (int i = 0; i < 9; i++)
Board.Add(new Cell());
}
public void CellClicked(Cell cell)
{
if (string.IsNullOrEmpty(cell.Content))
{
cell.Content = currentPlayer;
currentPlayer = currentPlayer == "X" ? "O" : "X";
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
3. MainWindow.xaml.cs (Codebehind mit DataContext)¶
public partial class MainWindow : Window
{
public GameViewModel ViewModel { get; }
public MainWindow()
{
InitializeComponent();
ViewModel = new GameViewModel();
DataContext = ViewModel;
}
}
4. MainWindow.xaml (UI mit DataBinding)¶
<Window x:Class="TicTacToeDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TicTacToeDemo"
Title="Tic Tac Toe" Height="400" Width="400">
<Window.Resources>
<DataTemplate DataType="{x:Type local:Cell}">
<Button FontSize="32" Margin="5"
Content="{Binding Content, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Click="CellButton_Click"/>
</DataTemplate>
</Window.Resources>
<UniformGrid x:Name="GameBoard" Rows="3" Columns="3" ItemsSource="{Binding Board}" />
</Window>
UniformGrid
braucht kein explizitesItemsControl
. Wenn du das machen willst, könntest duItemsControl
+UniformGrid
alsItemsPanel
verwenden.
5. EventHandler im Code-Behind¶
private void CellButton_Click(object sender, RoutedEventArgs e)
{
if (sender is Button button && button.DataContext is Cell cell)
{
ViewModel.CellClicked(cell);
}
}
✅ Vorteile des DataBindings in diesem Setup:¶
- Trennung von UI und Logik (MVVM-Prinzip).
- Einfache Wiederverwendbarkeit und Testbarkeit.
- UI aktualisiert sich automatisch bei Änderungen am Datenmodell.