Reputation: 1761
I have several hundred brushes defined in Application.xaml. These are shared resources for several user controls in project. They all have patterned keys: ch_YSD, ch_HJU, ch_IYO...
I try to use these brushes in datatemplate. In datatemplate I have access to variable part of brushes keys, that is in data template I can get YSD, HJU, IYO, etc. as string.
How can I bind to particular brush resource from xaml?
Currently I have such solution: in style use data trigger to set required property to specified brush depending on bound string (variable part of keys). I am not satisfied with this solution because list of brushes in Application.xaml will increase often.
I am not sure if I should go for code behind as I want to use memory saving benefit of shared resources in Application.xaml.
Upvotes: 1
Views: 359
Reputation: 3848
It's a kind of task that looks simple, but actually has no perfect solution (or I didn't find it). The first approach is to use value converter. But it doesn't work! We need to set resource reference which can't be done correctly by converter. So, I think the right way is attached behavior. But you should know about limitation: there can be only one property to apply resource to. Perhaps you could avoid this limitation depending on your requirements.
Attached behavior allows you to reference resource with a specified named to a specified dependency property:
public static class BrushResourceKeyBehavior
{
#region ResourceKey Property
public static readonly DependencyProperty ResourceKeyProperty = DependencyProperty.RegisterAttached(
"ResourceKey", typeof(object), typeof(BrushResourceKeyBehavior),
new FrameworkPropertyMetadata(OnResourceKeyChanged));
public static object GetResourceKey(DependencyObject dependencyObject)
{
return dependencyObject.GetValue(ResourceKeyProperty);
}
public static void SetSource(DependencyObject dependencyObject, object value)
{
dependencyObject.SetValue(ResourceKeyProperty, value);
}
#endregion
#region TargetProperty Property
public static readonly DependencyProperty TargetPropertyProperty = DependencyProperty.RegisterAttached(
"TargetProperty", typeof(DependencyProperty), typeof(BrushResourceKeyBehavior),
new FrameworkPropertyMetadata(OnTargetPropertyChanged));
public static DependencyProperty GetTargetProperty(DependencyObject dependencyObject)
{
return (DependencyProperty)dependencyObject.GetValue(TargetPropertyProperty);
}
public static void SetTargetProperty(DependencyObject dependencyObject, DependencyProperty value)
{
dependencyObject.SetValue(TargetPropertyProperty, value);
}
#endregion
private static void OnResourceKeyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var targetProperty = GetTargetProperty(dependencyObject);
if (targetProperty != null)
{
if (e.NewValue == null)
{
dependencyObject.ClearValue(targetProperty);
}
else
{
SetResourceReference(dependencyObject, targetProperty);
}
}
}
private static void OnTargetPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var oldValue = e.OldValue as DependencyProperty;
var newValue = e.NewValue as DependencyProperty;
if (oldValue != null)
{
dependencyObject.ClearValue(oldValue);
}
if (newValue != null)
{
SetResourceReference(dependencyObject, newValue);
}
}
private static void SetResourceReference(DependencyObject dependencyObject, DependencyProperty targetProperty)
{
var fe = dependencyObject as FrameworkElement;
if (fe != null)
{
fe.SetResourceReference(targetProperty, String.Format("ch_{0}", GetResourceKey(fe)));
}
else
{
var fce = dependencyObject as FrameworkContentElement;
if (fce != null)
{
fce.SetResourceReference(targetProperty, String.Format("ch_{0}", GetResourceKey(fce)));
}
}
}
}
Behavior can be used in XAML like the following:
<ItemsControl>
<Border local:BrushResourceKeyBehavior.Source="YSD"
local:BrushResourceKeyBehavior.TargetProperty="Border.Background"
Height="20"/>
<Border local:BrushResourceKeyBehavior.Source="HJU"
local:BrushResourceKeyBehavior.TargetProperty="Border.Background"
Height="20"/>
<Border local:BrushResourceKeyBehavior.Source="IYO"
local:BrushResourceKeyBehavior.TargetProperty="Border.Background"
Height="20"/>
</ItemsControl>
The code above is equivalent to:
<ItemsControl>
<Border Background="{DynamicResource ch_YSD}"
Height="20"/>
<Border Background="{DynamicResource ch_HJU}"
Height="20"/>
<Border Background="{DynamicResource ch_IYO}"
Height="20"/>
</ItemsControl>
Upvotes: 2