WPF 4.5 – Part 9 : binding to static properties UPDATED

, , 13 Comments

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:

  1. Create a static event handler for by property, named PropertyNameChanged.
  2. 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 E

Besides 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,

 

13 Responses

  1. Josh

    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.

    • jmix90

      18/10/2011 7 h 17 min

      It surely is ! Just try the snippet I give 🙂 What did you try exactly ?

      • Josh

        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! 🙂

    • Bruno Chappe

      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

  2. Daniel

    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 ?

  3. Mohamed Safwat

    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?

Comments are closed.