3

I am using WPF Localization Extension in my program. To reduce the ammount of similar resources (like "Length in meters", "Length in Kilometers" and so on) I would like to use placeholders and set them in XAML Code.

I thought about something like this

The value for the resource "UI_Length"

Length in {0}

In CodeBehind this is quite easy with String.Format

String.Format(
        LocalizeDictionary.Instance.GetLocalizedObject("UI_Length", null, LocalizeDictionary.Instance.Culture).ToString(), 
       "Meters");

But how can I add another String or even another resource entry in XAML?

<Label x:Name="Label" Content="{lex:LocText Key=UI_Length}, Meters" HorizontalAlignment="Left" VerticalAlignment="Top"/>

According to Binding placeholder from resource file in WPF it seems to be possible but I am not able to get it running for a label

[Update]

I managed to add two Resource values. The trick was to add a TextBlock in the Label.Content. see Link

        <Label  Grid.Row="1">
            <Label.Content>
                <TextBlock>
                    <TextBlock.Text>
                        <MultiBinding StringFormat="{}{0} [{1}]">
                            <Binding Source="{lex:LocText Key=UI_Length}" />
                            <Binding Source="{lex:LocText Key=UI_MeterShort}" />
                        </MultiBinding>
                    </TextBlock.Text>
                </TextBlock>
            </Label.Content>
        </Label>

But I still have a problem here. On Startup this works fine but when I try to switch the language with

LocalizeDictionary.Instance.Culture = new CultureInfo("de-DE");

I get a "Binding cannot be changed after it has been used." Error. Is there a similar way to use severall Resource values and still be able to switch the language during runtime?

[Update 2]

I tried the suggestion from Liero but still have an issue here.

In my MainView I set the DataContext to MainViewModel. The VM has a instance of LocalizedTexts. After changing the Language in the ViewModel I call the RaisePropertiesChanged() but I don't get any output.

The TextBlock is bound to the LengthInMeters

<TextBlock Text="{Binding LocalizedTexsts.LengthInMeters}" />

I am calling the RaisePorpertyChanged after changing the language:

    public void ChangeLanguage(string culture)
    {
         LocalizeDictionary.Instance.Culture = new CultureInfo(culture);
         _localizedTexts.RaisePropertyChanged();
    }

LengthInMeters looks like this:

public string LengthInMeters
{
    get
    {
        return String.Format(
                   LocalizeDictionary.Instance.GetLocalizedObject(
                       "UI_Length", null,
                       LocalizeDictionary.Instance.Culture).ToString(),
                   LocalizeDictionary.Instance.GetLocalizedObject(
                       "UI_Meters", null,
                       LocalizeDictionary.Instance.Culture).ToString()
               );
    }
}

But the TextBlock stays empty.

Community
  • 1
  • 1

1 Answers1

3

First of all, you can shorten the binding like this:

<TextBlock Text="{Binding Source={lex:LocText Key=UI_MeterShort}, 
                          StringFormat={lex:LocText Key=UI_Length}}" />

Second, if you want to update the binding, when language changes, then you need to find the TextBlock in codebehind and update the binding manually using BindingExpression.UpdateTarget() method. But that's strange, isn't it?

Alternatively, you could refresh entire window by removing all it's content and creating new. I mean something like removing a UserControl from the window and creating new UserControl.

However, both solutions are weird, because it is just workarounding problems caused by misuse of the bindings in WPF. Let me suggest better approach.

public class LocalizedTexts: INotifyPropertyChanged
{
   public event PropertyChanged;


   public LengthInMeters { get { return String.Format(...); } }

   public LengthInKilometers {get { return String.Format(...); } }


   public void RaisePropertiesChanged()
   {
      foreach (var property in this.GetType().GetProperties())
      {
         PropertyChanged(this, new PropertyChangedEventArgs(property.Name))
      }
   }
}

Now you can databind to LocalizedTexts and when language changes, just call RaisePropertiesChanged().

USAGE:

<Application.Resources>
    <l:LocalizedTexts x:Key="LocalizedTexts" />
</Application.Resources>

<TextBlock Text="{Binding LengthInMeters, Source={StaticResource LocalizedTexts}}" />


<!-- instead of using StaticResource, you can add l:LocalizedTexts  as a property to your viewmodel -->
<TextBlock Text="{Binding LocalizedTexts.LengthInMeters}" />
Liero
  • 25,216
  • 29
  • 151
  • 297
  • I tried your suggested approach but still have an issue here. What I've done is: In my MainView I set the DataContext to MainViewModel. The VM has a instance of LocalizedTexts. After changing the Language in the ViewModel I call the RaisePropertiesChanged() but I don't get any output. => please take a look at Update 2 for further details. – WaldemarCoding Aug 26 '16 at 21:32