Trick To Use StaticResource With Path
Overview
How awesome would it be if we could use StaticResource extension with property path support? It would rock! We all know that Binding supports property path but what about StaticResource. In this article I will show you how to implement a StaticResource with property path.
A user asked recently a question how to create a resource for example a color keyed as "MyLightColor" and then use one of the ARGB values inside another resource with different key.
Just to visualize the issue for you guys I will post following pseudo code:
<Window.Resources>
<Color x:Key="MyLightColor">Yellow</Color>
<Color x:Key="AnotherColor" A="MyLightLightColor.A" .... />
....
But how to get that Path "MyLightColor.A" working?
WPF offers us two ways to reference a resource. One is the StaticResource and another is DynamicResource but none of those will let us reference a color's member. Therefore we will need to trick a little bit. Btw, what are StaticResource and DynamicResource anyway? They are extension in wpf an I will not stay too long on explaining them in depth however let me just show you the major differences.
- The major difference between static and dynamic resources is “static resource will evaluate the resource only once while dynamic resource will be evaluated every time the resource needed”.
- Dynamic resource has more performance overhead than static resources because it look up for resources every time it requested or needed.
- Static resource is faster but it takes little more time to load page or window than dynamic resource because dynamic resources are loaded when you actually used those.
- If resource is defined on the element’s resources and the same element property is using the resource defined inside its resources then static resource not applied because it should appears afterwards. The same thing is valid for dynamic resource.
For those who wish to read more here is the link:
http://msdn.microsoft.com/en-us/library/ms750613(v=vs.110).aspx
What is that Path anyway?
Property paths in WPF as we all know from Binding allow us to place a string value as key which will WPF system use later to find and return certain property's value. Paths in WPF do not only support characters and dots in their syntax but also special characters like "[" or "(".
Here is an example:
<TextBlock Text="{Binding ... Path=(Validation.Errors)[0].Exception.Message }">
Its complicated right? If you wish to read more about property paths in XAML here is a link to them:
http://msdn.microsoft.com/en-us/library/cc645024(v=vs.95).aspx
The tricky trick.
Here is complete code:
public class StaticResourcePath : StaticResourceExtension
{
public PropertyPath Path
{
get;
set;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
// First get the value from StaticResource
object o = base.ProvideValue(serviceProvider);
return (Path == null ? o : PathEvaluator.Evaluate(o, Path));
}
class PathEvaluator : DependencyObject
{
/// <summary>
/// This dummy will hold the end result.
/// </summary>
private static readonly DependencyProperty DummyProperty =
DependencyProperty.Register("Dummy", typeof(object),
typeof(PathEvaluator), new UIPropertyMetadata(null));
public static object Evaluate(object source, PropertyPath path)
{
PathEvaluator d = new PathEvaluator();
BindingOperations.SetBinding(d, DummyProperty, new Binding(path.Path) { Source = source });
// Force binding to give us the desired value defined in path.
var result = d.GetValue(DummyProperty);
// Clear the binding to leave nice memory footprints
BindingOperations.ClearBinding(d, DummyProperty);
return result;
}
}
}
I am a type of guy who rather likes the users to check out the code for themselves instead of me writing big essays explaining each step.
That would be it. Try it out guys.
And here is how you can use your extended StaticResource:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="550" Width="625"
xmlns:local="clr-namespace:WpfApplication1">
<Window.Resources>
<SolidColorBrush x:Key="redBackgroundBrush" Color="Red"/>
<SolidColorBrush x:Key="redishBackgroundBrush">
<SolidColorBrush.Color>
<Color A="{local:StaticResourcePath ResourceKey=redBackgroundBrush, Path=Color.A}"
R="{local:StaticResourcePath ResourceKey=redBackgroundBrush, Path=Color.R}"
B="{local:StaticResourcePath ResourceKey=redBackgroundBrush, Path=Color.B}">
<Color.G>
20
</Color.G>
</Color>
</SolidColorBrush.Color>
</SolidColorBrush>
</Window.Resources>
<Grid Background="{StaticResource redishBackgroundBrush}">
</Grid>
</Window>
Pretty smooth isn't it? If you have any further questions feel free to post a comment right below.