Jun
16
2009

Using Data Binding with Static Resources in WPF

If you’ve been using WPF for more than a couple of weeks, I’m sure you wrote code like this a lot of times: you define a resource and reference it with StaticResource.

<Window x:Class="PSampaio.StaticResourcesWithBinding.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Width="400" Height="200">
	<Window.Resources>
		<SolidColorBrush x:Key="RedBrush" Color="#FFFF0000" />
	</Window.Resources>
	<Grid>
		<TextBlock Text="Hello World" FontSize="48" Foreground="{StaticResource RedBrush}" />
	</Grid>
</Window>

However, if you applications are getting increasingly more complex, then it can start to get more tricky. In this post, I’ll be showing how to use resources for which you don’t know in design time the key to use in xaml (maybe because you’re loading them dynamically), by binding them to view model properties (which, by the way, you should be using).

Setting the scene

Suppose you’re indeed loading the resources dynamically, like so:

<Application x:Class="PSampaio.StaticResourcesWithBinding.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    StartupUri="Window1.xaml">
	<Application.Resources>
		<SolidColorBrush x:Key="RedBrush" Color="#FFFF0000" />
	</Application.Resources>
</Application>

Yes, yes, I know. You can reference that resource the usual way, but this is just sample code, so assume you can’t. We’re also using a view model that contains a property with the key for the resource you want to use (that maybe you got that after loading the resource dynamically). That model is being instanced from the window constructor. Again, let me stress that this is sample code that you need to adapt to fit your own circumstances.

public Window1()
{
	InitializeComponent();

	DataContext = new MyViewModel("RedBrush");
}
namespace PSampaio.StaticResourcesWithBinding
{
	public class MyViewModel
	{
		public string MyResourceKey { get; private set; }

		public MyViewModel(string myResourceKey)
		{
			MyResourceKey = myResourceKey;
		}
	}
}

Are you with me so far? Good. Lets move on!

The obvious solution

Your first reaction to this will be to actually use the binding in a StaticResource. You can go ahead and try it if you want to. You’ve get to something like this:

<Window x:Class="PSampaio.StaticResourcesWithBinding.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Width="400" Height="200">
	<Grid>
		<TextBlock Text="Hello World" FontSize="48" Foreground="{StaticResource {Binding MyResourceKey}}" />
	</Grid>
</Window>

This code will compile just fine, but if you run it, you’ll get an exception. This is because the StaticResource key is only evaluated when it’s needed, and when it tries to provide a value, it can’t find a resource named ‘{System.Windows.Data.Binding}’. So we’ll have to find another way, since this clearly won’t work.

A better solution

The only place were we can make this work is the markup itself, so lets start from the top, with the syntax we want to use, and go from there all the way to the bottom. We want this to work:

<Window x:Class="PSampaio.StaticResourcesWithBinding.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Width="400" Height="200">
	<Grid>
		<TextBlock Text="Hello World" FontSize="48" Foreground="{BindableStaticResource {Binding MyResourceKey}}" />
	</Grid>
</Window>

In order for this to work, we need to create a new markup extension, so lets do just that. Create a new class that derives from the original StaticResourceExtension, called BindableStaticResourceExtension. Notice that the suffix is optional in XAML, and that you’ll need to add the namespace prefix in the markup. Create it with 2 constructors, one empty, and one with the Binding. You should have this class for now.

using System.Windows;
using System.Windows.Data;

namespace PSampaio.StaticResourcesWithBinding
{
	public class BindableStaticResource : StaticResourceExtension
	{
		public Binding Binding { get; set; }

		public BindableStaticResource()
		{
		}

		public BindableStaticResource(Binding binding)
		{
			Binding = binding;
		}
	}
}

Now the tricky part, when and how do you get the actual resource key, and what exactly do you do with it. The ‘when’ part is the easy one: there’s one method you can override public override object ProvideValue(IServiceProvider serviceProvider) that the framework will call in order to get the value. In this method, you have to evaluate the binding and get its result (the ‘how’) and set it to the ResourceKey in the base StaticResourceExtension class (the ‘what to do’). After that, you can delegate the rest of the work to the base class and let it provide it’s value. I’ll first show the code, and then go through it.

private static readonly DependencyProperty DummyProperty;

static BindableStaticResource()
{
	DummyProperty = DependencyProperty.RegisterAttached("Dummy",
										     typeof (Object),
										     typeof (DependencyObject),
										     new UIPropertyMetadata(null));
}

public override object ProvideValue(IServiceProvider serviceProvider)
{
	var target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
	var targetObject = (FrameworkElement)target.TargetObject;

	MyBinding.Source = targetObject.DataContext;
	var DummyDO = new DependencyObject();
	BindingOperations.SetBinding(DummyDO, DummyProperty, MyBinding);

	ResourceKey = DummyDO.GetValue(DummyProperty);

	return base.ProvideValue(serviceProvider);
}

It may take a little to fully understand what’s going on here, but it’s fairly easy (once you figure it out). You need to query the service provider for an IProvideValueTarget. The returned object has two properties: TargetObject and TargetProperty. This tells you which property of which object we’re evaluating the binding for. You set the source of the binding to the data context of the target object, meaning “look here for the path you want”. This is the only use you’ll have for the target object

You then set the binding on a dummy DependencyObject, with a dummy property. If you use TargetProperty here, the binding operation will try to convert the resulting value to the type of the property. In this case, it would try to convert the string “RedBrush” to an actual Brush object, as that’s the type of TargetProperty (Foreground is a Brush). After this, all you need to do is get the value from the dummy property.

Now that ResourceKey contains the resource key we need, just call base.ProvideValue(serviceProvider) and let StaticResourceExtension try and find a resource with that key. Hope this helps!

You can download the code from this link.

Related Posts

About the Author: Pedro Sampaio

I'm an UX Software Engineer at FARO Technologies, in Portugal. I work mainly with .NET technologies, such as WPF, during the day. Off work, I develop web application in Rails. I also have experience with ASP.NET MVC, Test and Behavior Driven Development, and agile methodologies, namely Scrum.

16 Comments + Add Comment

  • Very neat – thank you a bunch for this!

  • I haven’t been able to make this work when creating the View through a DataTemplate for a ViewModel. The DataContext is not set (null) when ProvideValue gets called. Do you happen to know a work around for this?

    Thanks in advance.

    • Jorge,

      I too am trying to use this BindableStaticResource in a View through a DataTemplate.
      The DataContext of the targetObject is always null.

      Did you find a fix for this?

      Jay.

  • Hi,

    This solution looks great and exactly what I was looking for, but I just have one small problem. After doing this I cannot open the designer, as in it throws an error saying that “No constructor for type ‘BindableStaticResource’ has 1 parameters” even though when I run it it works fine and there exists that constructor. Do you have any solution for this?

    Thanks for the post it was very helpful.

    Thanks,
    Kaushik R

  • Awesome! Thanks Pedro, I’ve just spent a couple of days trying to figure out how to do this. I’m a bit disappointed that it can’t be done in pure XAML but at least it’s a painless and straightforward solution.

  • nice work! nice thinking actually – I guess it could be done otherwise (as everything in WPF), some ‘pivot’ resource outside or something – but this looks like a neat solution to the problem

  • The code provided is not building: Error 1 Assembly ‘PSampaio.StaticResourcesWithBinding’ was not found. Verify that you are not missing an assembly reference. Also, verify that your project and all referenced assemblies have been built. C:\Documents and Settings\jane prusakova\My Documents\Downloads\PSampaio.StaticResourcesWithBinding\PSampaio.StaticResourcesWithBinding\PSampaio.StaticResourcesWithBinding\Window1.xaml 4 38 PSampaio.StaticResourcesWithBinding

    Same error when I implement your suggested objects in my project.
    Building against 3.5.

  • Great job! I really was disappointed finding that I cannot use binding for resource keys and the implementation is great.
    The issue mentioned by Kaushik R wasn’t reproduced on my coputer under MS VS 2010. However I noticed that the WPF designer complains about ArgumentNullException thrown when the StaticResourceExtension.ResourceKey property is assigned null – and at design time this happens in most cases.
    I fixed this issue by changing ProvideValue method and reimplementing the ResourceKey property. The modified code looks like this:

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
    var target = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
    var targetObject = (FrameworkElement)target.TargetObject;

    MyBinding.Source = targetObject.DataContext;
    var DummyDO = new DependencyObject();
    BindingOperations.SetBinding(DummyDO, DummyProperty, MyBinding);

    ResourceKey = DummyDO.GetValue(DummyProperty);

    return ResourceKey != null ? base.ProvideValue(serviceProvider) : null;
    }

    public new object ResourceKey
    {
    get
    {
    return base.ResourceKey;
    }
    set
    {
    if (value != null)
    base.ResourceKey = value;
    }
    }

  • Thank you this was exactly what I needed.

  • Great! Thanks!

  • Thank you for this code, it is very helpful; I’ve been looking for a better way to bind resources to my viewmodels for a while. One question though, using this approach, is it possible to update the binding when the underlying property changes?

    • I came up with a way to make this work, although I suspect there has to be a “better” way to do it. My approach was to copy the targetObject from the ProvideValue method into the underlying view model. This allows me to apply the new templates as needed directly from the VM.

      • Turns out this was easier achieved with a DataTrigger; guess the obvious was escaping me :P Still a useful bit of code though so thanks again.

  • Hello,Pedro
    When I use your solution in Setter, I got an error message:
    “Key cannot be null”
    And it did not take ProvideValue method
    Can you help solve
    Tanks!

    • LittleW: I’m having the same problem too:

      .
      .
      .

  • Porreiro pá! :)

Leave a comment