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:
FolderItemImage
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.
Data binding is a mechanism in WPF applications that provides a simple and easy way for Windows Runtime apps to display and interact with data. In this mechanism, the management of data is entirely separated from the way data. Data binding allows the flow of data between UI elements and data object on user interface.
In Extensible Application Markup Language (XAML), the ResourceDictionary class is typically an implicit collection element that is the object element value of several Resources properties, when given in property element syntax. For details on implicit collections in XAML, see XAML Syntax Terminology.
A resource dictionary is a repository for XAML resources, such as styles, that your app uses. You define the resources in XAML and can then retrieve them in XAML using the {StaticResource} markup extension and {ThemeResource} markup extension s. You can also access resources with code, but that is less common.
I write following markup extension to allow binding to resourceKey in general case
using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Markup;
namespace Mersoft.Mvvm.MarkupExtensions
{
public class ResourceBinding : MarkupExtension
{
#region Helper properties
public static object GetResourceBindingKeyHelper(DependencyObject obj)
{
return (object)obj.GetValue(ResourceBindingKeyHelperProperty);
}
public static void SetResourceBindingKeyHelper(DependencyObject obj, object value)
{
obj.SetValue(ResourceBindingKeyHelperProperty, value);
}
// Using a DependencyProperty as the backing store for ResourceBindingKeyHelper. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ResourceBindingKeyHelperProperty =
DependencyProperty.RegisterAttached("ResourceBindingKeyHelper", typeof(object), typeof(ResourceBinding), new PropertyMetadata(null, ResourceKeyChanged));
static void ResourceKeyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var target = d as FrameworkElement;
var newVal = e.NewValue as Tuple<object, DependencyProperty>;
if (target == null || newVal == null)
return;
var dp = newVal.Item2;
if (newVal.Item1 == null)
{
target.SetValue(dp, dp.GetMetadata(target).DefaultValue);
return;
}
target.SetResourceReference(dp, newVal.Item1);
}
#endregion
public ResourceBinding()
{
}
public ResourceBinding(string path)
{
this.Path = new PropertyPath(path);
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
var provideValueTargetService = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget));
if (provideValueTargetService == null)
return null;
if (provideValueTargetService.TargetObject != null &&
provideValueTargetService.TargetObject.GetType().FullName == "System.Windows.SharedDp")
return this;
var targetObject = provideValueTargetService.TargetObject as FrameworkElement;
var targetProperty = provideValueTargetService.TargetProperty as DependencyProperty;
if (targetObject == null || targetProperty == null)
return null;
var binding = new Binding();
#region binding
binding.Path = this.Path;
binding.XPath = this.XPath;
binding.Mode = this.Mode;
binding.UpdateSourceTrigger = this.UpdateSourceTrigger;
binding.Converter = this.Converter;
binding.ConverterParameter = this.ConverterParameter;
binding.ConverterCulture = this.ConverterCulture;
if (this.RelativeSource != null)
binding.RelativeSource = this.RelativeSource;
if (this.ElementName != null)
binding.ElementName = this.ElementName;
if (this.Source != null)
binding.Source = this.Source;
binding.FallbackValue = this.FallbackValue;
#endregion
var multiBinding = new MultiBinding();
multiBinding.Converter = HelperConverter.Current;
multiBinding.ConverterParameter = targetProperty;
multiBinding.Bindings.Add(binding);
multiBinding.NotifyOnSourceUpdated = true;
targetObject.SetBinding(ResourceBindingKeyHelperProperty, multiBinding);
return null;
}
#region Binding Members
/// <summary> The source path (for CLR bindings).</summary>
public object Source
{
get;
set;
}
/// <summary> The source path (for CLR bindings).</summary>
public PropertyPath Path
{
get;
set;
}
/// <summary> The XPath path (for XML bindings).</summary>
[DefaultValue(null)]
public string XPath
{
get;
set;
}
/// <summary> Binding mode </summary>
[DefaultValue(BindingMode.Default)]
public BindingMode Mode
{
get;
set;
}
/// <summary> Update type </summary>
[DefaultValue(UpdateSourceTrigger.Default)]
public UpdateSourceTrigger UpdateSourceTrigger
{
get;
set;
}
/// <summary> The Converter to apply </summary>
[DefaultValue(null)]
public IValueConverter Converter
{
get;
set;
}
/// <summary>
/// The parameter to pass to converter.
/// </summary>
/// <value></value>
[DefaultValue(null)]
public object ConverterParameter
{
get;
set;
}
/// <summary> Culture in which to evaluate the converter </summary>
[DefaultValue(null)]
[TypeConverter(typeof(System.Windows.CultureInfoIetfLanguageTagConverter))]
public CultureInfo ConverterCulture
{
get;
set;
}
/// <summary>
/// Description of the object to use as the source, relative to the target element.
/// </summary>
[DefaultValue(null)]
public RelativeSource RelativeSource
{
get;
set;
}
/// <summary> Name of the element to use as the source </summary>
[DefaultValue(null)]
public string ElementName
{
get;
set;
}
#endregion
#region BindingBase Members
/// <summary> Value to use when source cannot provide a value </summary>
/// <remarks>
/// Initialized to DependencyProperty.UnsetValue; if FallbackValue is not set, BindingExpression
/// will return target property's default when Binding cannot get a real value.
/// </remarks>
public object FallbackValue
{
get;
set;
}
#endregion
#region Nested types
private class HelperConverter : IMultiValueConverter
{
public static readonly HelperConverter Current = new HelperConverter();
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return Tuple.Create(values[0], (DependencyProperty)parameter);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
#endregion
}
}
Usage
<Image Source="{local:ResourceBinding ImageResourceKey}"/>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With