Search code examples
c#wpfbindingdynamicresource

Binding to resource key, WPF


I have a ResourceDictionary with some images:

<BitmapImage UriSource="..\Images\Bright\folder-bright.png"
             x:Key="FolderItemImage" />

I've create a HierarchicalTemplate for treeview items like the following:

<HierarchicalDataTemplate ItemsSource="{Binding VisibleChildren}"
                          DataType="{x:Type st:StructureTreeItem}">
    <StackPanel Orientation="Horizontal">
        <TextBlock Text="{Binding ImageResourceKey}" />
        <Image x:Name="iIcon2" Source="{DynamicResource FolderItemImage}"/>
        <Image x:Name="iIcon"
               Source="{DynamicResource {Binding ImageResourceKey}}"/>
    </StackPanel>
</HierarchicalDataTemplate>

Now, when item is displayed:

  • Textblock displays FolderItemImage
  • First image is shown
  • Second image is not shown.

The whole idea is to set item images to ones stored in resources, but the technique presented above unfortunately won't work and now I know, why:

<Image x:Name="iIcon3" Width="16" Height="16" Margin="0, 1, 3, 1" >
    <Image.Source>
        <DynamicResource ResourceKey="{Binding ImageResourceKey}" />
    </Image.Source>
</Image>

Result:

An unhandled exception of type 'System.Windows.Markup.XamlParseException' occurred in PresentationFramework.dll

Additional information: A 'Binding' cannot be set on the 'ResourceKey' property of type 'DynamicResourceExtension'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.

So I have to rephrase my question: how can I convert some data (a resource key, perhaps?) stored in model to a dynamic resource? It has to be a dynamic resource, because I'm certain it may change during runtime.


Solution

  • It cannot be done directly. There's another way though involving an attached property:

    public static class ImageHelper {
    
        private static void SourceResourceKeyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
    
            var element = d as Image;
            if (element != null) {
    
                element.SetResourceReference(Image.SourceProperty, e.NewValue);
            }
        }
    
        public static readonly DependencyProperty SourceResourceKeyProperty = DependencyProperty.RegisterAttached("SourceResourceKey",
            typeof(object),
            typeof(ImageHelper),
            new PropertyMetadata(String.Empty, SourceResourceKeyChanged));
    
        public static void SetSourceResourceKey(Image element, object value) {
    
            element.SetValue(SourceResourceKeyProperty, value);
        }
    
        public static object GetSourceResourceKey(Image element) {
    
            return element.GetValue(SourceResourceKeyProperty);
        }
    }
    

    And then:

    <Image local:ImageHelper.SourceResourceKey="{Binding SomeValue}" />