Im dritten Teil Windows Presentation Foundation 4.5 Serie beschäftigen wir uns mit der Bindung an statischen Eigenschaften. Dies war – ohne Umwege – bis inklusive WPF 4.0 nicht möglich. Da es jedoch immer wieder eine entsprechende Anforderung gibt, wurde dies mit Version 4.5 erweitert und diese Möglichkeit zur Verfügung gestellt.
Um statische Eigenschaften mit einem Two-Way-Binding zu versehen, können statische Ereignisse verwendet werden. Diese müssen sich an eine von zwei Signaturen halten. Die erste Signatur ist wie folgt:
public static event EventHandler MyPropertyChanged;
Dabei ist “MyProperty” mit dem Namen der tatsächlichen Eigenschaft zu ersetzen. Die zweite Möglichkeit besteht in einem generischen Ansatz (der an das Ereignis PropertyChanged aus INotifyPropertyChanged angelehnt ist):
public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;
Wichtig: Zu beachten ist, dass die beiden Möglichkeiten mit unterschiedlichen Ereignis-Argumenten arbeiten. Die erste Variante setzt auf die Klasse EventArgs, Variante 2 auf PropertyChangedEventArgs. Dies ist für eine erfolgreiche Implementierung zu berücksichtigen.
Sehen wir uns die Implementierung einer statischen Klasse StaticSettings mit zwei statischen Eigenschaften Value1 und Value2 an:
1: public static class StaticSettings
2: {
3: private static string value1;
4: private static string value2;
5:
6: public static string Value1
7: {
8: get { return value1; }
9: set
10: {
11: if (value1 == value)
12: return;
13: value1 = value;
14: RaiseValue1Changed();
15: }
16: }
17:
18: public static event EventHandler Value1Changed;
19:
20: private static void RaiseValue1Changed()
21: {
22: EventHandler handler = Value1Changed;
23: if (handler != null)
24: handler(null, new EventArgs());
25: }
26:
27: public static string Value2
28: {
29: get { return value2; }
30: set
31: {
32: if (value2 == value)
33: return;
34: value2 = value;
35: RaiseStaticPropertyChanged("Value2");
36: }
37: }
38:
39: public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;
40:
41: public static void RaiseStaticPropertyChanged(string propertyName)
42: {
43: EventHandler<PropertyChangedEventArgs> handler = StaticPropertyChanged;
44: if (handler != null)
45: handler(null, new PropertyChangedEventArgs(propertyName));
46: }
47: }
Dabei wird die Update-Notification der Eigenschaft Value1 nach der ersten Variante, die der Eigenschaft Value2 nach der zweiten Variante implementiert. Welche Variante eingesetzt wird, hängt von der Anzahl der Eigenschaften ab. Variante 1 wird bei mehreren Eigenschaften schnell unübersichtlich, wodurch der generische Ansatz zu bevorzugen ist.
Anschließend erfolgt die Deklaration der View:
1: <Window x:Class="DevTyr.Wpf45.StaticPropertyBinding.MainWindow"
2: xmlns="schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="schemas.microsoft.com/winfx/2006/xaml"
4: xmlns:local="clr-namespace:DevTyr.Wpf45.StaticPropertyBinding"
5: Title="DevTyr - WPF 4.5 - Static Property Binding" Height="350" Width="525">
6:
7: <Grid>
8: <Grid.ColumnDefinitions>
9: <ColumnDefinition Width="Auto" MaxWidth="250"/>
10: <ColumnDefinition Width="*"/>
11: </Grid.ColumnDefinitions>
12:
13: <Grid.RowDefinitions>
14: <RowDefinition Height="Auto"/>
15: <RowDefinition Height="Auto"/>
16: <RowDefinition Height="Auto"/>
17: <RowDefinition Height="*"/>
18: <RowDefinition Height="Auto"/>
19: </Grid.RowDefinitions>
20:
21: <TextBlock Text="PropertyNameChanged"
22: TextWrapping="Wrap"/>
23: <TextBox Text="{Binding Path=(local:StaticSettings.Value1)}"
24: Grid.Column="1"/>
25:
26: <TextBlock Text="StaticPropertyChanged"
27: TextWrapping="Wrap"
28: Grid.Row="1"/>
29: <TextBox Text="{Binding Path=(local:StaticSettings.Value2)}"
30: Grid.Column="1"
31: Grid.Row="1"/>
32:
33: <StackPanel Grid.Row="4"
34: Grid.ColumnSpan="2"
35: Orientation="Horizontal">
36: <Button Content="Update property values"
37: Click="OnRefreshClick"/>
38: <Button Content="Reset"
39: Click="OnResetClick"/>
40: <Button Content="Show current source data"
41: Click="OnShowDataClick"/>
42: </StackPanel>
43:
44: </Grid>
45: </Window>
Im Endeffekt wird hier nicht mehr gemacht, als zwei Textfelder (diese haben per Default eine Two-Way-Bindung) mit einer Bindung auf die beiden Eigenschaften der in den Ressourcen definierten Klasse zu versehen.
Wichtig: Zu beachten sind die runden Klammern, die für die Pfadangabe der Bindung verwendet werden. Durch diese “weiß” das Bindungssystem, dass nach dem Schema Klassenname.Eigenschaftsname geparst werden soll. Ohne runde Klammern wird nach dem Schema Eigenschaftsname.Eigenschaftsname verfahren.
Nicht verschrecken sollten folgende beiden Eigenheiten der Developer Preview:
Trotz der angezeigten Fehler kann die Anwendung trotzdem gestartet werden. Offensichtlich wird die neue Bindungs-Syntax noch nicht erkannt. In den weiteren Versionen wird es hierfür wohl einen entsprechenden Fix bzw. eine Erweiterung geben.
Um das Verhalten testen zu können, finden sich auch einige Schaltflächen auf der Oberfläche (auf die Verwendung von MVVM wird der Einfachheit halber verzichtet).
Hier die Eventhandler:
private void OnRefreshClick(object sender, RoutedEventArgs e)
{
StaticSettings.Value1 = "Updated";
StaticSettings.Value2 = "Updated";
}
private void OnResetClick(object sender, RoutedEventArgs e)
{
StaticSettings.Value1 = "";
StaticSettings.Value2 = "";
}
private void OnShowDataClick(object sender, RoutedEventArgs e)
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat("Value1: {0}\n", StaticSettings.Value1);
sb.AppendFormat("Value2: {0}\n", StaticSettings.Value2);
MessageBox.Show(sb.ToString());
}
Nach einem Start der Anwendung können über die erste Schaltfläche im Hintergrund die Werte der beiden Eigenschaften verändert werden. Wie nachfolgend zu sehen, spiegeln sich diese in der Oberfläche wider. Test bestanden.
Hinweis: Wer mit der Erweiterung x:Static arbeiten wollte, wird eine Enttäuschung erleben. Hier verhält es sich so, dass die Ereignisse bei der Änderung der Source nicht ausgewertet werden. Dies ist das Standardverhalten und wird auch in WPF 4.5 nicht geändert.
Das hier besprochene Beispiel kann nachfolgend herunter geladen werden.