Startseite  Inhaltsverzeichnis  <<  >>  

Kapitel 10 - Data Binding

10.1 Übersicht

Datensensitive Kontrollelemente stellen einen bestimmten Teil Ihrer grafischen Darstellung abhängig von einem verbundenen Datencontainer dar. So kann der Inhalt einer TextBox vom aktuellen Inhalt eines Datensatzes einer Datenbanktabelle abhängen oder der Inhalt einer Listbox ergibt sich aus den Daten eines eingebetteten XML-Dokuments. Alle UI-Elemente in Avalon können mit einer Datenquelle verbunden werden. Dazu wird der Wert einer Eigenschaft mit einer Datenquelle verbunden bzw. daran gebunden.

Die Funktionalität der Datengebundenheit wird durch die Avalon Data Services ermöglicht. Die Daten können über Objekte, XML-Dokumenten (extern oder eingebettet), Web Services oder Datenbanken mittels ADO.NET vorliegen. Bezüglich der Anbindung gibt es drei Formen der Datenbindung. Diese können durch eine Einstellung festgelegt werden, sofern die entsprechenden Einstellungen zulässig sind. Welche Form der Datenbindung möglich ist, hängt auch von der Datenquelle und dem Kontrollelement ab.

Wert Beschreibung
OneWay Änderungen an der Quelle werden im Ziel sichtbar, aber nicht umgekehrt. Die Datenquelle wird auf diese Weise vor Änderungen geschützt.
TwoWay
Änderungen werden jeweils auch auf der anderen Seite sichtbar.
OneTime
Änderungen wirken sich weder auf der einen noch auf der anderen Seite aus. Die Werte werden aus der Datenquelle einmalig ausgelesen. Danach wird die Datenbindung getrennt. 

Zur Herstellung einer Datenbindung ist die Angabe des Quell- und Zielobjekts sowie der verbundenen Eigenschaften notwendig.


Datenbindung an Compound-Eigenschaften

Die einfachste Form der Datenbindung erfolgt an eine Eigenschaft eines anderen Kontrollelements. Um Bind als Element zu verwenden, muss die zu bindende  Eigenschaft als Compound-Eigenschaft definiert sein wie unter
<TextBox>
  <TextBox.Text>
  </TextBox.Text>
</TextBox>
Jetzt können Sie sich auf die Eigenschaft TextBox.Text beziehen (auch als Bindung zu einer dependency property bezeichnet). Standardmäßig unterstützt die TextBox die Datenbindung TwoWay. Ist die Bindung also erst einmal hergestellt, bewirken Änderungen in beiden Textboxen im folgenden Beispiel, dass sich der Wert der anderen ändert. Ändern Sie den Wert des Attributs BindType nach OneWay, TwoWay und OneTime, um die verschiedenen Effekte zu untersuchen.

Um zwei Textboxen zu koppeln:
  1. Vergebe für die zu referenzierende TextBox einen Namen über das Attribut ID, z.B. tbOriginal.
  2. Erstelle ein Element mit der Compound-Eigenschaft, auf die referenziert werden soll, im Beispiel auf die Eigenschaft Text.
  3. Lege den Inhalt eines Elements über eine Datenbindung fest. Dazu wird das Element Bind genutzt.
  4. Vergebe für das Attribut ElementID den Namen des zu referenzierenden Elements.
  5. Lege über das Attribut Path den Pfad zur gebundenen Eigenschaft fest.
  6. Überschreibe gegebenenfalls den standardmäßigen Biundungstyp.
Wenn Sie jetzt in der oberen TextBox einen Text eingeben, wird dieser in der unteren TextBox übernommen.

Beispiel: DataBinding1.xaml

<DockPanel xmlns="http://schemas.microsoft.com/2003/xaml">
  <TextBox ID="tbOriginal" DockPanel.Dock="Top">
    <TextBox.Text>
      Original ...
    </TextBox.Text>
  </TextBox>
  <TextBox ID="tbKopie" DockPanel.Dock="Top">
    <TextBox.Text>
      <Bind ElementID="tbOriginal" Path="Text" BindType="OneWay"/>
    </TextBox.Text>
  </TextBox>
</DockPanel>
Eine Kurzschreibweise besteht darin, der Eigenschaft die Bindung über *Bind() zuzuweisen. Die einzelnen Attribute von Bind werden dann mit Semikolon getrennt, und die Werte dürfen nicht in Anführungszeichen eingeschlossen werden. Im Beispiel werden durch zwei weitere Textboxen die Bindtypen OneWay und der Defaultwert (TwoWay) gezeigt.
<TextBox ID="tbKopie" DockPanel.Dock="Top" 
Text="*Bind(ElementID=tbOriginal;Path=Text;BindType=OneWay)" />
<TextBox DockPanel.Dock="Top"
Text="*Bind(ElementID=tbOriginal;Path=Text)" />
oder Bindungen zwischen Button und TextBox
<Button  Content="Button 1" ID="Button1" />
<Text TextContent="*Bind(Path=Content; ElementID=Button1)" />
// oder
<TextBox ID="tbOriginal" Text="Original ..." />
<Button  Content="*Bind(ElementID=tbOriginal; Path=Text)" />

Die Klasse Bind

Die Klasse befindet sich im Namespace System.Windows.Data.


Eigenschaft Beschreibung
BindFlags
Hiermit können Sie festlegen, ob beim Aktualisieren der Datenwerte Ereignisse ausgelöst werden (Werte: NotifyOnTransfer, NotifyOnValidationError).
BindType
Legt den Bindungstyp über die Werte der gleichnamigen Aufzählung fest (Werte: TwoWay, OneWay, OneTime, Default). Der Standard-BindType hängt von den entsprechenden Möglichkeiten des Elements ab.
DataSource Definiert die Datenquelle der Daten.
ElementIDHiermit legen Sie die ID des Quellelements der Daten fest. Alternativ geben Sie den String "Parent" an, um auf das Elternelement zu verweisen. Durch die Angabe von "Previous" verweisen Sie auf vorhergehende Element. Angaben von Parent/Parent usw. sind erlaubt.
PathSpezifiziert die Eigenschaft der Datenquelle, welche die Daten liefert.
SourceLegt das Quellobjekt fest.
UpdateSourceTriggerBei einer TwoWay-Binding werden bei Änderungen beide Elemente aktualisiert. Die Richtung vom Ziel zur Quelle kann über diese Eigenschaft explizit gesteuert werden. Die Aufzählung UpdateSourceTrigger enthält dazu die Werte PropertyChanged (Update bei Änderungen des Eigenschaftswertes), LostFocus (Update beim Verlust des Fokus) und Explicit (Update erst beim Aufruf von Binding.UpdateSource()).
XPathAngabe des Pfades zu XML-Datenelementen mittels XPath-Syntax.


Beispiel: Bindungen aktualisieren

<Button Content="Bindung aktualisieren" Width="200" Height="30" Click="btnClick3" />
      <def:Code>
        <![CDATA[
       
        public void btnClick3(object sender, RoutedEventArgs e)
        {
            Binding bd = tbKopie.GetBinding(TextBox.TextProperty);
            bd.UpdateSource();
        }
       
        ]]>
      </def:Code>
     
      <TextBox ID="tbOriginal" Background="LightBlue">
    <TextBox.Text>
      Original ...
    </TextBox.Text>
  </TextBox>
   <TextBox ID="tbKopie" Text="*Bind(ElementID=tbOriginal;Path=Text;BindType=TwoWay;UpdateSourceTrigger=Explicit)" />

10.x XML-Daten anbinden

Die Datenbindung an XML-Daten erfolgt ein einen XML-Knoten oder einer Collection von Knoten. Die XML-Daten können dazu direkt im Markup stehen (XML-Data-Island) oder Sie beziehen sich auf eine separate Datei über das Attribut Source. Durch die Angabe des XPath-Ausdrucks zu Beginn der Liste kann der verwendete Umfang des Dokuments zentral festgelegt werden. So können beispielsweise nur die Mitarbeiter zurückgegeben werden, deren Personalnummer größer als 100 ist. Eine XML-Datenquelle wird dazu im Ressourcenteil eines Elements definiert und später unter ihrem Namen angesprochen.

Die leere Angabe des Namespaces xmlns des Wurzelelements ist notwendig, damit später die XPath-Ausdrücke korrekt ausgewertet werden können. Standardmäßig würde <MitarbeiterListe> den Namesraum des übergeordneten Elements erben.

Beispiel: MAListe.xml
<?xml version="1.0" ?>
<MitarbeiterListe>
  <Mitarbeiter ID="1">
    <Name>Meier</Name>
    <Vorname>Klaus</Vorname>
  </Mitarbeiter>
  <!-- weitere Mitarbeiter -->
</MitarbeiterListe>



 Beispiel: DataBinding6.xaml
<DockPanel xmlns="http://schemas.microsoft.com/2003/xaml"
           xmlns:def="Definition">
  <DockPanel.Resources>
    <XmlDataSource def:Name="Daten" XPath="/Mitarbeiter">
      <Mitarbeiter xmlns="">
        <MA ID="0">
          <Name>Anton</Name>
          <Alter>44</Alter>
        </MA>
        <MA ID="1">
          <Name>Meier</Name>
          <Alter>19</Alter>
        </MA>
        <MA ID="2">
          <Name>Mueller</Name>
          <Alter>24</Alter>
        </MA>
        <MA ID="3">
          <Name>Schulze</Name>
          <Alter>35</Alter>
        </MA>
      </Mitarbeiter>
    </XmlDataSource>
    <Style def:Name="MAStyle">
      <ContentPresenter/>
      <Style.VisualTree>
        <Text FontSize="Small">
          <Canvas>
            <Text Canvas.Left="5" TextContent="*Bind(XPath=Name)"/>
            <Text Canvas.Left="100" TextContent="*Bind(XPath=Alter)"/>
            <Text Canvas.Left="200" TextContent="*Bind(XPath=@ID)"/>
          </Canvas>
        </Text>
      </Style.VisualTree>
    </Style>
  </DockPanel.Resources>

  <FlowPanel>
    <ListBox ItemStyle="{MAStyle}" Width="400">
      <CollectionContainer Collection="*Bind(DataSource={Daten};XPath=MA)" />
    </ListBox>
  </FlowPanel>
</DockPanel>



<XmlDataSource def:Name="Mitarbeiter3" 
Source="MAListe.xml"
XPath="/MitarbeiterListe">
<XmlDataSource def:Name="Mitarbeiter2"
Source="file:///E:/Daten/avalon/examples/MAListe.xml"
XPath="/MitarbeiterListe">

10.x Konvertierer

Über Konvertierer (früher Transformierer) kann die Datendarstellung beeinflusst werden. String-Daten können dann z.B. als Chart angezeigt werden, nachdem Sie als Zahlen interpretiert wurden. Mittels des Interfaces IValueConverter können Sie eigene Konvertierer definieren. Sie müssen dazu Methoden zur Konvertierung in beiden Richtungen implementieren. Die Methode Convert wird aufgerufen, wenn ein Wert zugewiesen werden soll. Das Zurückschreiben in eine Datenquelle kann über ConvertBack beeinflusst werden. Der Parameter sender enthält den Wert des Elements. Der Parameter Typ kann zum Test auf den verlangten Datentyp verwendet werden.

Beispiel: Farbverwalter

In einer XAML-Datei wird der Hintergrund einer Zeichenfläche aufgrund des Textes einer TextBox gesetzt. Die Eigenschaft Background des Canvas wird mit der Eigenschaft Text einer TextBox verknüpft. Ändert sich der Inhalt der TextBox, wird der neue Wert dem Konvertierer übergeben. Dieser überpfüt den Typ und Wert und liefert dann einen entsprechenden Rückgabewert. Geben Sie also in die TextBox RED ein, wird die Zeichenfläche Rot gefärbt. Geben Sie LightBlue ein, wird die Zeichenfläche geld gefüllt. Es sind also beliebige Manipulationen möglich. Statt Strings können Sie z.B. auch Zahlen als FarbCode verwenden.
using System.Globalization;
namespace DFrischa
{
  public class StringToColor : IValueConverter
  {
    public object Convert(object sender, Type type, object para, CultureInfo ci)
    {
      if(type.Name == "Brush")
      {
        if (sender.ToString() == "RED")
        return Brushes.Red;
        if (sender.ToString() == "LightBlue")
          return Brushes.Yellow;
      }
      return Brushes.White;
    }
    public object ConvertBack(object sender, Type type, object para, CultureInfo ci)
    {
      return null;
    }
  }
}
Damit der Konvertierer verwendet werden kann, muss eine Referenz im Ressourcen-Teil definiert werden. Dazu wird für den Konvertierer ein Name vergeben und der Typ, also der Klassenname, angegeben. Im Element Bind wird der Konvertierer über das Attribut Converter gesetzt.
<DockPanel>
  <DockPanel.Resources>
    <ValueConverterSource def:Name="vcs" TypeName="DFrischa.StringToColor" />
  </DockPanel.Resources>  
  <TextBox ID="FarbAuswahlTB" Text="RED" />
  <Canvas Width="20">
      <Canvas.Background>
          <Bind  Path="Text" ElementID="FarbAuswahlTB" Converter="{vcs}" />
      </Canvas.Background>         
  </Canvas>
</DockPanel>

10.x Eigenschaften an Objekteigenschaften binden

Eine Eigenschaft kann an den Wert einer Eigenschaft eines Objekts gebunden werden, wobei das Objekt als C#-Code vorliegt. Im folgenden Beispiel wird außerdem der Einsatz der DataContext-Eigenschaft gezeigt. Wird in einem Element keine Datenquelle angegeben, wird im darüberliegenden Element nach einer belegten Eigenschaft DataContext gesucht. Auf diese Weise muss z.B. nur das Wurzelelement mit einer Datenquelle verbunden werden. Alle deren Elemente suchen dann automatisch in dessen Datenquelle.

namespace DFrischa
{
  public class PropObj
  {
    public string StringFarbe
    {
      get
      {
        return "LightBlue";
      }
   }
    public SolidColorBrush BrushFarbe
    {
      get
      {
        return Brushes.AliceBlue;
      }
   }
    public string Name
    {
      get
      {
        return "Ein vielsagender Text...";
      }
    }
}
}
Im Attribut TypeName wird der Klassenname des Objekts angegeben, von dem dann eine Instanz erzeugt wird und dessen Eigenschaftswerte ausgelesen werden. Befindet sich dieser Typ in einer anderen Assembly, muss deren Name mit Komma getrennt hinter dem Typnamen angegeben werden, z.B. TypeName="DFrischa.PropObj,ExterneAssembly".
<DockPanel xmlns="http://schemas.microsoft.com/2003/xaml" xmlns:def="Definition">
  <DockPanel.Resources>
    <ObjectDataSource def:Name="MeinObjekt" TypeName="DFrischa.PropObj" />
 </DockPanel.Resources>
  <DockPanel>
  <DockPanel DataContext="*Bind(DataSource={MeinObjekt})">
  <Button Background="*Bind(Path=StringFarbe;BindType=OneWay)"
           Content="*Bind(Path=Name;BindType=OneWay)" Width="200" Height="30" />
  </DockPanel>
</DockPanel>

10.x Bindungen im Code erzeugen

Datenbindungen können natürlich auch direkt im Code erzeugt werden. Der Hauptunterschied zur Definition einer Bindung in XAML ist, dass Sie die Eigenschaftstypen über Objekte und spezielle Eigenschaften der betreffenden Klassen festlegen müssen. Um beispielsweise eine Bindung zur Eigenschaft Text einer TextBox herzustellen, erstellen Sie ein PropertyPath-Objekt und übergeben als Parameter den Namen der Klasse und der Eigenschaft, gefolgt vom Präfix Property, also TextBox.TextProperty. Dieselbe Vorgehensweise wird beim Herstellen der Bindung über die Methode SetBindung() benötigt, in der als erster Parameter die Eigenschaft des Zielelements angegeben werden muss.

Beispiel: Beschriftung einer Schaltfläche an den Inhalt einer TextBox binden
<Button Width="200" Height="30" ID="Button1" Content="KlickKlack" Click="btnClick" />
<TextBox ID="TextBox1">
  BasisText
</TextBox>


public void btnClick(object sender, RoutedEventArgs e)
{
  Bind bnd = new Bind();
  bnd.Source = TextBox1;
  bnd.Path = new PropertyPath(TextBox.TextProperty);
  bnd.BindType = BindType.TwoWay;
  bnd.FallbackValue = "Test";
  Button1.SetBinding(Button.ContentProperty, bnd);
}