One of the new features in WPF 4.5 is support for binding to
static properties. In addition to the binding syntax itself,
there's new support for static property change notification.
In many cases, it is still advisable to use a Singleton pattern
or another approach for binding. However, there are cases when your
application design requires binding to static properties. In those
cases, WPF should kindly step out of the way and let you accomplish
your task the way you've architected it.
Two Approaches to Change Notification
There are two ways to notify the binding system of property
changes, both of which follow the same pattern as their
instance-based relatives.
The first is to create a static event named
<propertyName>Changed. Raise this event when the property
changes. Here's an example:
public static class AppSettings
{
public static event EventHandler StorageFolderChanged;
private static string _storageFolder;
public static string StorageFolder
{
get { return _storageFolder; }
set
{
if (value != _storageFolder)
{
_storageFolder = value;
if (StorageFolderChanged != null)
StorageFolderChanged(null, EventArgs.Empty);
}
}
}
}
The static property name in this example is StorageFolder. The
event name is therefore StorageFolderChanged. Note that nothing
useful is passed as part of the event - there's no instance to
provide and no event args.
The second approach is to create a single event which is the
static version of the INPC PropertyChanged event named
StaticPropertyChanged. This requires using the
System.ComponentModel namespace to pull in the
PropertyChangedEventArgs definition.
public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;
private static int _speedFactor;
public static int SpeedFactor
{
get { return _speedFactor; }
set
{
if (value != _speedFactor)
{
_speedFactor = value;
NotifyStaticPropertyChanged("SpeedFactor");
}
}
}
private static void NotifyStaticPropertyChanged(string propertyName)
{
if (StaticPropertyChanged != null)
StaticPropertyChanged(null, new PropertyChangedEventArgs(propertyName));
}
For this, I followed the same pattern I do with instance
classes, pulling the change notification out into a separate
function.
Code-Behind
I have the tiniest bit of code-behind here, just to set the
initial values.
public MainWindow()
{
InitializeComponent();
AppSettings.SpeedFactor = 10;
AppSettings.StorageFolder = @"d:\Foo\Bar";
}
Binding
Here's the XAML UI for this sample app. There are two TextBlock
elements bound to properties of the AppSettings class. In addition,
there's a single TextBox bound to one of the same properties using
ToWay binding.
<Window x:Class="Wpf45StaticBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Wpf45StaticBinding"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize"
Value="35" />
</Style>
<Style TargetType="TextBox">
<Setter Property="FontSize"
Value="35" />
</Style>
</Window.Resources>
<Grid>
<StackPanel>
<TextBox x:Name="StorageFolderField"
Text="{Binding Path=(local:AppSettings.StorageFolder), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<TextBlock x:Name="StorageFolderDisplay"
Text="{Binding Path=(local:AppSettings.StorageFolder)}" />
<TextBlock x:Name="SpeedFactorDisplay"
Text="{Binding Path=(local:AppSettings.SpeedFactor)}" />
</StackPanel>
</Grid>
</Window>
Note the modification to the binding syntax. You'll use
parentheses around the namespace, class, and property because, as
Sam Bent explained to me "They tell us to parse the path as
ClassName.PropertyName rather than PropertyName.PropertyName."
Oh, and Ignore those. Umm, and ignore this too.
As is sometimes the case during preview builds, the IDE/parser
and the code base are not quite in sync. In this case, it doesn't
recognize the cool new static binding syntax and is trying to parse
it as an attached property. Never fear! This example still works
:)
Go ahead and run it. Change the value in the TextBox. You should
see the value in the TextBlock update, just as you would with
normal instance binding.
You can also add in another TextBox to test out the numeric
field and its own change notification.
Binding to static properties fills a need many of our customers
have, and it's one more way that WPF gets out of the way of the
solution you're trying to implement. It just works.