Unfortunately you cannot force controls set up with markup extensions in XAML to reevaluate their properties using those extensions - the evaluation is only done once upon parsing XAML file. What basically happens behind the scenes is this:
- Your extension is instantiated
ProvideValue
method is called on the created instance and the returned value is used on the target control
- The reference to the created instance is not stored (or is a weak reference, I'm not sure), so your extension is ready for GC
You can confirm that your extension is only used once by defining a finalizer (desctructor) and setting a breakpoint in it. It will be hit soon after your page is loaded (at least it was in my case - you may need to call GC.Collect()
explicitly). So I think the problem is clear - you cannot call ProvideValue
on your extension again at an arbitrary time, because it possibly no longer exists.
However, there is a solution to your problem, which doesn't even need making any changes to your XAML files - you only need to modify the TranslateExtension
class. The idea is that under the hood it will setup proper binding rather than simply return a value.
First off we need a class that will serve as a source for all the bindings (we'll use singleton design pattern):
public class Translator : INotifyPropertyChanged
{
public string this[string text]
{
get
{
//return translation of "text" for current language settings
}
}
public static Translator Instance { get; } = new Translator();
public event PropertyChangedEventHandler PropertyChanged;
public void Invalidate()
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(Binding.IndexerName));
}
}
The goal here is that Translator.Instance["Label_Text"]
should return the translation that your current extension returns for "Label_Text"
. Then the extension should setup the binding in the ProvideValue
method:
public class TranslateExtension : MarkupExtension
{
public TranslateExtension(string text)
{
Text = text;
}
public string Text { get; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
var binding = new Binding
{
Mode = BindingMode.OneWay,
Path = new PropertyPath($"[{Text}]"),
Source = Translator.Instance,
};
return binding.ProvideValue(serviceProvider);
}
}
Now all you need to do is to call Translator.Instance.Invalidate()
every time the language is changed.
Note that using {i18n:Translate Label_Text}
will be equivalent to using {Binding [Label_Text], Source={x:Static i18n:Translator.Instance}}
, but is more concise and saves you the effort of revising your XAML files.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…