Verfasst von: bletra | 11. Mai 2011

WPF: Von Spaghetticode zu MVVM – Teil 6 von 9

In dieser Artikelserie möchte ich eine einfache Applikation, in eine testbare Applikation nach dem MVVM-Pattern umwandeln:

  1. Beispielapplikation mit Spaghetticode
  2. Refactoring 1: Model
  3. MVC, MVP, Presentation Model
  4. MVVM
  5. ICommand
  6. Binding
  7. Refactoring 2: ViewModel
  8. Refactoring 3: View
  9. Zusammenfassung

Binding

Beim Binding geht es darum, ein GUI-Element an Daten oder einen Command eines anderen Objekts zu binden. Dieses andere Objekt kann auch ein GUI-Element sein. Im Folgenden betrachte ich zunächst den Fall der Datenbindung, am Ende des Artikels gehe ich kurz auf das Binden von Commands ein.

Bei der Datenbindung sollen Änderungen zwischen den gebundenen Objekten propagiert werden. Die Elemente der GUI bilden das Ziel (Target) und das andere Objekt die Quelle (Source). Sind beides GUI-Elemente, so ist das Element, bei dem ein Binding definiert wird das Ziel – die Bindung muss nicht doppelt definiert werden. Es sind vier verschiedene Modi zu unterscheiden:

  • OneWay: Änderungen der Quelle führen zu Änderungen im Ziel.
  • TwoWay (default): Änderungen warden in beide Richtungen propagiert.
  • OneWayToSource: Umgekehrt zu OneWay, Änderungen des Ziels führen zu Änderungen der Quelle.
  • OneTime: Bei der Initialisierung des Ziels werden die Daten der Quelle abgefragt, Änderungen werden nicht propagiert.

Die technologische Basis bilden entsprechende Events. Die Eigenschaft eines GUI-Elements, dessen Wert an die Eigenschaft (Property) eines anderen Objekts gebunden werden soll, muss ein sogenanntes DependencyProperty sein. Allgemein stellt ein DependencyProperty „eine Eigenschaft dar, die durch Methoden wie Formatierung, Datenbindung, Animation und Vererbung festgelegt werden kann.“ Bei der Bindung muss nun noch festgelegt werden, welches Ereignis des Ziels eine Mitteilung der Änderung an die Quelle (im Beispiel PropertyChanged) bewirkt. Das folgende Beispiel zeigt die Synchronisation zweier Textboxen:

<TextBox Name="txtTarget" Text="{Binding Mode=TwoWay, ElementNa-me=txtSource, Path=Text, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Name="txtSource" />

Mit der Eigenschaft Path wird festgelegt, auf welche Eigenschaft von txtSource sich die Bindung bezieht. Dies muss nicht ebenfalls die Text-Eigenschaft sein, wie folgendes Beispiel zeigt:

<TextBox Name="txtTarget" Text="{Binding Mode=TwoWay, Element-Name=txtSource,Path=Background, UpdateSourceTrigger=PropertyChanged}" />
<TextBox Name="txtSource"  />
<Button Content="Red" Click="Button_Click" />

Mit CodeBehind

private void Button_Click(object sender, RoutedEventArgs e)
{
    txtSource.Background = new SolidColorBrush(Colors.Red);
}

Soll die Bindung zu einer Eigenschaft eines Nicht-GUI-Objekt erfolgen, so muss erstens dieses Objekt bekannt gemacht werden (ElementName funktioniert nun nicht): Hierfür gibt es verschiedene Möglichkeiten, in Bezug auf MVVM setzen wir die Eigenschaft DataContext des MainWindows/UserControls auf eine Instanz der Quelle. Auch hierfür gibt es wieder unterschiedliche Möglichkeiten, einfach kann dies nach InitializeComponent() erfolgen:

DataContext = new DemoViewModel();

Zweitens benötigen wir zur  Weiterleitung von Änderungen der Quelle (Property von DemoViewModel) an das Ziel (GUI) ebenfalls ein entsprechendes event. Dieses wird über das Interface INotifyPropertyChanged definiert:

public interface INotifyPropertyChanged
{
    event PropertyChangedEventHandler PropertyChanged;
}
public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);

Als Basis für ein zu bindenden Objekt bietet sich folgende Klasse an:

public abstract class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Im Setter und allen in anderen Methoden, die die zu bindende Eigenschaft (hier Greeting) verändert,  noch NotifyPropertyChanged aufgerufen werden, wie folgendes Beispiel zeigt:

public class DemoViewModel: BaseViewModel
{
  private string _greeting = "hi folks";
  public string Greeting
  {
    get { return _greeting; }
    set { _greeting = value; NotifyPropertyChanged("Greeting"); }
  }
}

Die Eigenschaft Greeting lässt sich nun wie folgt in XAML an die DependencyProperty Text einer Textbox binden:

<TextBox Name="txtTarget2" Text="{Binding Mode=TwoWay, Path=Greeting, Up-dateSourceTrigger=PropertyChanged}" PresentationTrace-Sources.TraceLevel="High" />

Mit der Eigenschaft PresentationTraceSources bekommen Sie ggf. mehr Debug-Informationen im Output-Window. Binding ist eine tolle Sache, aber lausig zu debuggen und damit ist es leider schwer, Fehler im Binding zu finden. Ein Blick in das Output-Fenster während des Debuggens ist jedoch immer ein Versuch wert.

Sie müssen nicht notwendigerweise selbst das Interface INotifyPropertyChanged implementieren und sich um das Auslösen des PropertyChanged-Events kümmern, für Listen können Sie beispielsweise die Klasse ObservableCollection verwenden, die das Interface bereits implementiert.

Merke: Die Datenbindung erfordert …

  • ein Objekt (Quelle) einer Klasse, die INotifyPropertyChanged implementiert
  • ein Objekt (Ziel, GUI) mit einem DependencyProperty

Diesen Zusammenhang veranschaulicht die folgende Abbildung, die aus dem Artikel „DataBinding in WPF“ von Christian Mosers stammt:

Zu sehen ist auch ein ValueConverter. Auf diesen können wir im Allgemeinen verzichten, da das ViewModel ggf. die Werte anpasst. Nun haben wir alle Technologien eingeführt und können im nächsten Artikel das ViewModel implementieren.

Advertisements

Responses

  1. […] Demo-Applikation zusammenführen. Ich beginne mit dem ViewModel, das BaseViewModel, wie ich es in “WPF: Von Spaghetticode zu MVVM – Teil 6 von 9″ besprochen habe, erweitert. Das ViewModel verwendet die Klasse ExplicitlyObservableList, wie in […]

  2. […] Binding […]

  3. […] Binding […]


Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

Kategorien

%d Bloggern gefällt das: