Dima
Dima

Reputation: 1761

How to choose brush in XAML based on binding

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

Answers (1)

Marat Khasanov
Marat Khasanov

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

Related Questions