0
votes

I want to add dynamic items with a datatemplate that contains a TextBlock control, but the text of the TextBlock control will be selected from a XAML ResourceDictionary. The staticresource name will be obtained based on the result of the binding value.

How can I do that? I'm trying something like this, but doesn't works.

 <DataTemplate x:Key="languageItemTemplate">
            <ContentControl>
                <StackPanel>                    
                     <TextBlock Text="{StaticResource  {Binding ResourceName}}"></TextBlock>    
                     <TextBlock Text="{DynamicResource  {Binding ResourceName}}"></TextBlock>                    
                </StackPanel>
            </ContentControl>
 </DataTemplate>

UPDATE Thanks to Tobias, the fist option of his answer works. But I need to instance the converter first to get it work. Which one is the best idea to do that?

In the application_startup method and use it for all the application or in the Window.Resources of the window I use the converter?

Maybe a merge of both and do that on the Application.Resources?

thanks for your answer.

        private void Application_Startup(object sender, StartupEventArgs e)
        {
            LoadConverters();
        }
        private void LoadConverters()
        {
            foreach (var t in System.Reflection.Assembly.GetExecutingAssembly().GetTypes())
            {
                if (t.GetInterfaces().Any(i => i.Name == "IValueConverter"))
                {
                    Resources.Add(t.Name, Activator.CreateInstance(t));
                }
            }
        }

OR

<local:BindingResourceConverter x:Key="ResourceConverter"/>
<DataTemplate x:Key="languageItemTemplate">
            <ContentControl>
                <StackPanel>                    
                     <TextBlock Text="{Binding Name, Converter={StaticResource ResourceConverter }}" />                 
                </StackPanel>
            </ContentControl>
 </DataTemplate>

1

1 Answers

0
votes

If the resource is an application level resource you could simply use a converter to convert from the resource name to the actual object like this:

public class BindingResourceConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        string resourceKey = value as string;
        if (!String.IsNullOrEmpty(resourceKey))
        {
            var resource = Application.Current.FindResource(resourceKey);             
            if (resource != null)
            {
                return resource;
            }
        }
        return Binding.DoNothing;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

And use it like this:

<TextBlock Text="{Binding ResourceKey, Converter={StaticResource ResourceConverter}}" />

If the resource is in a local scope, we need a reference to the control to search its resources. You can get the resource name and the control by using an attached property:

public class TextBlockHelper
{
    public static readonly DependencyProperty TextResourceKeyProperty =
        DependencyProperty.RegisterAttached("TextResourceKey", typeof(string),
            typeof(TextBlockHelper), new PropertyMetadata(String.Empty, OnTextResourceKeyChanged));

    public static string GetTextResourceKey(DependencyObject obj)
    {
        return (string)obj.GetValue(TextResourceKeyProperty);
    }

    public static void SetTextResourceKey(DependencyObject obj, string value)
    {
        obj.SetValue(TextResourceKeyProperty, value);
    }

    private static void OnTextResourceKeyChanged(
        DependencyObject d,
        DependencyPropertyChangedEventArgs e)
    {
        string resourceKey = e.NewValue as string;
        if(d is TextBlock tb)
        {
            var r = tb.TryFindResource(resourceKey);
            if (r != null)
            {
                tb.Text = r.ToString();
            }
        }
    }
}

And you can use it like this:

<Grid>
    <Grid.Resources>
        <sys:String x:Key="SomeLocalResource">LocalResource</sys:String>
    </Grid.Resources>
    <TextBlock h:TextBlockHelper.TextResourceKey="{Binding ResourceKey}" />
</Grid>