There is two different ways to make a property bindable: implementing INotifyPropertyChanged is the most known solution, the other one is to create a event named PropertyNameChanged.
In WPF 4.0 there is no way to create a static property which can be used by a binding with property change notifications.
In this post, part of a serie on the WPF 4.5 new features, we’ll see how it can now be done!
The theory
As pointed out in the documentation page, there is, as for instance properties, two solution to achieve that:
- Create a static event handler for by property, named PropertyNameChanged.
- Create a generic event handler with an argument giving the name of the updated property.
It does not work with the x:Static extension 🙁
I first think that this will be an easy demo to build: I just have to use the x:Static extension as a source in a Binding and tada this is over! But life isn’t easy: it does not works.
On the contrary, I had to put an instance object on which my static property is defined in the resource and use it as the source of the Binding.
In the demo project, I have created an object named Repository which defines a static property named “Color”. I will use it to define a brush on a Rectangle so changes will be easy to watch.
[csharp]
public class Repository
{
static Repository() { UpdateColor(); }
static Random rnd = new Random((int)DateTime.Now.Ticks);
public static void UpdateColor()
{
Color = new Color()
{
A = 255,
R = (byte)rnd.Next(255),
G = (byte)rnd.Next(255),
B = (byte)rnd.Next(255),
};
}
//Color property is here, let’s see later how to define it…
}
[/csharp]
Then I put an instance of it in the application resources:
[xml]<Application.Resources>
<local:Repository x:Key="Repository" />
</Application.Resources>
[/xml]
And I create a binding on the static property of this instance:
[xml]<Rectangle>
<Rectangle.Fill>
<SolidColorBrush Color="{Binding Color,Source={StaticResource Repository}}"/>
</Rectangle.Fill>
</Rectangle>
[/xml]
UPDATED: why the static extension does not work ?
Sam Bent from the WPF team explained me via emails why the static markup extension cannot be used for this scenario. Here is a quote of his mail:
It’s not so strange.
<TextBlock Text=”abc”/> assigns “abc” to the property, no change propagation (doesn’t even make sense)
<TextBlock Text=”{StaticResource C}”/> assigns a string to the property. No change propagation.
<TextBlock Text=”{x:Static A.B}”/> assigns a string to the property. No change propagation.In general, most ways of assigning a value to the property don’t do change propagation. If you want something dynamic, you have to ask for it. There’s only two ways to do that:
<TextBlock Text=”{DynamicResource D}”/>: reacts to changes to the resource dictionary where D is defined
<TextBlock Text=”{Binding Path=E}”/>: reacts to changes to the source property EBesides the pay-for-play argument, there are two more reasons why {x:Static} doesn’t do change propagation:
1. Compatability: It didn’t in 4.0.
2.Functionality: Some (most?) people still want the 4.0 behavior, with no change propagation, if only because it’s cheaper. We’d need a way to get that, {x:StaticWithNoChangePropagation} or the like.
So in our case, we could have used this notation:
[xml]<Rectangle>
<Rectangle.Fill>
<SolidColorBrush Color="{Binding (local:Repository.Color)}"/>
</Rectangle.Fill>
</Rectangle>
[/xml]
The bracker are mandatory for it to work and the XAML processor complains something about attached properties which does not exists: just ignore it.
First solution: PropertyNameChanged event
As the property is named “Color”, I create a static event named “ColorChanged”. Each time the property value is updated, I raise it with an helper method. There is nothing more to do.
[csharp]
private static Color _color;
public static Color Color
{
get { return _color; }
set
{
if (_color == value) return;
_color = value;
RaiseColorChanged();
}
}
public static event EventHandler ColorChanged;
public static void RaiseColorChanged()
{
EventHandler handler = ColorChanged;
if (handler != null)
handler(null, EventArgs.Empty);
}
[/csharp]
As this is a static property, there is no instance so null is provided as a sender in the argument.
This solution is nice when you have only one property but can be fastidious when you have a lot of them because there is one event to define/raise for each property. This is why the second solution is often prefered.
Second solution: the generic event
The generic event is what you are used to with normal property.
The declaration of the event is usual. When you raise the event, you have to provide the name of the changed property to the event argument.
Here too, as there is no instance, null is provided as a sender in the argument.
[csharp]private static Color _color;
public static Color Color
{
get { return _color; }
set
{
if (_color == value) return;
_color = value;
RaiseStaticPropertyChanged("Color");
}
}
public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;
public static void RaiseStaticPropertyChanged(string propName)
{
EventHandler<PropertyChangedEventArgs> handler = StaticPropertyChanged;
if (handler != null)
handler(null, new PropertyChangedEventArgs(propName));
}
[/csharp]
This solution differs just a little from the non-static one : you can’t use string.empty as a property name to signal a change of every static properties values. If you do so you’ll, at the time I write this post, get an InvalidCastException: “Unable to cast object of type ‘PropertyRecord’ to type ‘ListenerList'”.
Additional thoughts
The demo – which can be found on my Dropbox folder after registration if you want to give me space 🙂 – works fine and the rectangle color is updated on each button’s click. But when I tried to add a TextBlock binded to the same property under the rectangle, I noticed that the rectangle color was nor more updated.
After some investigation, I found out that it seems that only the first binding in the XAML seems to be working. The other are no more updated…
Did I missed something ? If yes, please tell it in the comments !
Update: Sam told me that it will be fixed in the vNext.
Regards,
18/10/2011 0 h 16 min
Is it possible to bind to a STATIC CLASS's static property with WPF 4.5? I can't seem to get it to work.
18/10/2011 7 h 17 min
It surely is ! Just try the snippet I give 🙂 What did you try exactly ?
25/10/2011 23 h 53 min
I got it to work. I'm a WPF newb to begin with but I wasn't able to follow your example. For the record, this is what I ended up having to do: <StatusBarItem Content="{Binding Path=(p:Projector.Status)}"/>. Removing the Path= breaks it. Removing the parentheses breaks it. Thanks for the post — it at least got me on the right track! 🙂
27/10/2011 10 h 07 min
Thank you for your feedback 🙂 !
20/10/2016 12 h 03 min
This works great BUT it seems that the binding does not work if you're lacking the "Path=" into the binding notation, at least when using static binding for DataTriggers
04/04/2012 9 h 34 min
In my side , the <TextBlock Margin="10" DockPanel.Dock="Bottom" Text="{Binding Color,Source={StaticResource Repository}}" /> changed when i change the color by clicking the button "ChangeTheStaticValueClick"
Is it what is not work in your side ?
04/04/2012 10 h 30 min
It seems too since the new release 🙂
19/06/2012 8 h 53 min
I wrote a quick 1, 2, 3 step process on this at the following link. (To Admin, sorry I posted this moments ago but mistyped my information).
Static Binding in WPF .Net 4.5 is a feature I have wanted for years.
http://www.oceansidesw.com/wiki/C-Net-4-5-XAML-WP…
25/11/2015 8 h 24 min
your solution works well but XMAL process complains from error that the used property is not dependency property and it give me invalid markup although the application run well so i achieve the function i want but i can't continue the work because of invalid markup i don't know it;s a bug or what
what's your suggestion?
13/06/2016 10 h 48 min
It's working like a charm, thank you 🙂