WPF localization: DynamicResource with StringFormat?

I am doing localization in .NET 4 with ResourceDictionary. Does anyone have a solution for using a value with a string format?

For example, let's say I have a value with the key "SomeKey":

<ResourceDictionary ...>
    <s:String x:Key="SomeKey">You ran {0} miles</s:String>
</ResourceDictionary>

      

Using it in the TextBlock:

<TextBlock Text="{DynamicResource SomeKey}" />

      

How could I concatenate, for example, an integer with a SomeKey value as a format string?

+3


source to share


3 answers


So, I finally came up with a solution that allows me to have formatted strings in my ResourceDictionary and be able to dynamically change the language at runtime. I think it could be improved, but it works.

This class converts the resource key to its value from the ResourceDictionary:

public class Localization
{
    public static object GetResource(DependencyObject obj)
    {
        return (object)obj.GetValue(ResourceProperty);
    }

    public static void SetResource(DependencyObject obj, object value)
    {
        obj.SetValue(ResourceProperty, value);
    }

    // Using a DependencyProperty as the backing store for Resource.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty ResourceProperty =
        DependencyProperty.RegisterAttached("Resource", typeof(object), typeof(Localization), new PropertyMetadata(null, OnResourceChanged));

    private static void OnResourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        //check if ResourceReferenceExpression is already registered
        if (d.ReadLocalValue(ResourceProperty).GetType().Name == "ResourceReferenceExpression")
            return;

        var fe = d as FrameworkElement;
        if (fe == null)
            return;

        //register ResourceReferenceExpression - what DynamicResourceExtension outputs in ProvideValue
        fe.SetResourceReference(ResourceProperty, e.NewValue);
    }
}

      

This class allows you to use a value from a ResourceDictionary as a format parameter in String.Format ()

public class FormatStringConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        if (values[0] == DependencyProperty.UnsetValue || values[0] == null)
            return String.Empty;

        var format = (string)values[0];
        var args = values.Where((o, i) => { return i != 0; }).ToArray();

        return String.Format(format, args);
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

      

Example 1 . In this example, I use the FormatStringConverter on a MultiBinding to convert its Binding collection to the desired output. If, for example, the value "SomeKey" is "Object ID - {0}" and the value "Id" is "1", then the output will be "Object ID is 1".



                <TextBlock ap:Localization.Resource="SomeKey">
                    <TextBlock.Text>
                        <MultiBinding Converter="{StaticResource formatStringConverter}">
                            <Binding Path="(ap:Localization.Resource)" RelativeSource="{RelativeSource Self}" />
                            <Binding Path="Id" />
                        </MultiBinding>
                    </TextBlock.Text>
                </TextBlock>

      

Example 2 . In this example, I am using a bind with a converter to change the resource key to something more verbose to prevent collisions. If, for example, I have an enum Enum.Value (displayed as "Value" by default), I use a converter to attach its namespace to make the key more unique. Thus, the value becomes "My.Enums.Namespace.Enum.Value". The Text property will then resolve with any value "My.Enums.Namespace.Enum.Value" in the ResourceDictionary.

        <ComboBox ItemsSource="{Binding Enums}"
                  SelectedItem="{Binding SelectedEnum}">
            <ComboBox.ItemTemplate>
                <DataTemplate>
                    <TextBlock ap:Localization.Resource="{Binding Converter={StaticResource enumToResourceKeyConverter}}"
                               Text="{Binding Path=ap:Localization.Resource), RelativeSource={RelativeSource Self}}"/>
                </DataTemplate>
            </ComboBox.ItemTemplate>
        </ComboBox>

      

Example 3 . In this example, the key is a literal and is only used to find its corresponding value in the ResourceDictionary. If, for example, "SomeKey" is "SomeValue", then it will simply return "SomeValue".

                    <TextBlock ap:Localization.Resource="SomeKey"
                               Text="{Binding Path=ap:Localization.Resource), RelativeSource={RelativeSource Self}}"/>

      

0


source


You need to bind to the ViewModel.Value somehow and then use the (nested) binding to the format string.

If you only have one value:

<TextBlock 
  Text="{Binding Path=DemoValue, StringFormat={StaticResource SomeKey}}" />        

      



If you also have {1}

etc., you will need MultiBinding.

Edit:

If you really want to change languages ​​in live form, then the sane way is probably to do all the formatting in the ViewModel. Anyway, I rarely use StringFormat or MultiBinding in MVVM.

+3


source


If you are trying to bind and format the Miles property to "TextBlock" you can do the following:

<TextBlock Text="{Binding Miles, StringFormat={StaticResource SomeKey}}"/>

+1


source







All Articles