Home > Development > Using Data Binding with Static Resources in WPF

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.

Shout it kick it on DotNetKicks.com

  1. December 2nd, 2009 at 15:16 | #1

    Very neat – thank you a bunch for this!

  1. June 16th, 2009 at 16:17 | #1
  2. July 6th, 2009 at 02:26 | #2