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:
- Vergebe für die zu referenzierende TextBox einen Namen
über das Attribut ID, z.B. tbOriginal.
- Erstelle ein Element mit der Compound-Eigenschaft, auf die
referenziert werden soll, im Beispiel auf die Eigenschaft Text.
- Lege den Inhalt eines Elements über eine Datenbindung fest.
Dazu wird das Element Bind genutzt.
- Vergebe für das Attribut ElementID den Namen des zu
referenzierenden Elements.
- Lege über das Attribut Path den Pfad zur gebundenen
Eigenschaft fest.
- Ü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.
|
ElementID | Hiermit 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. |
Path | Spezifiziert die Eigenschaft der Datenquelle, welche die Daten liefert. |
Source | Legt das Quellobjekt fest. |
UpdateSourceTrigger | Bei 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()). |
XPath | Angabe 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);
}