重新评估由标记扩展名计算的xaml页中的所有值 [英] Re-evaluate all values in xaml page calculated by a markup-extension
问题描述
在xaml页面上的xamarin应用程序中,我正在使用xaml扩展名加载本地化字符串(详细信息在此处).例如:
In a xamarin app on a xaml page I am loading localized strings using a xaml extension (the details are described here). For example:
<Label Text={i18n:Translate Label_Text}/>
现在,我希望用户能够在运行时(使用选择器)更改应用程序的语言.如果发生这种情况,我想立即更改语言.
Now, I want the user to be able to change the language of the app at runtime (using a picker). If that happens, I want to change the language immediately.
我可以以某种方式重新加载所有翻译的文本吗?
Can I somehow reload all translated texts?
我可以删除所有页面并重新创建它们,但是我试图避免这种情况.
I could delete all pages and recreate them, but I am trying to avoid that.
我还可以将所有本地化文本绑定到页面模型中的字符串.但这对于真正的静态字符串来说是很多不必要的代码.
I could also bind all localised texts to strings in the pages model. But that is a lot of unnecessary code for truly static strings.
推荐答案
不幸的是,您不能强制使用 XAML 中的标记扩展名设置的控件使用这些扩展名重新评估其属性-评估仅执行一次解析 XAML 文件时.幕后发生的基本上是这样的:
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:
- 您的扩展程序已实例化 在创建的实例上调用
-
ProvideValue
方法,并在目标控件上使用返回的值 - 未存储对创建的实例的引用(或者我不确定是弱引用),因此您的扩展程序已准备就绪,可以用于GC
- 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
您可以通过定义终结器(desctructor)并在其中设置断点来确认扩展仅使用一次.页面加载后很快就会被点击(至少在我看来是这样-您可能需要显式调用GC.Collect()
).因此,我认为问题很明显-您不能在任意时间再次在扩展名上调用ProvideValue
,因为它可能不再存在.
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.
但是,有一个解决问题的方法,它甚至不需要对您的 XAML 文件进行任何更改-您只需要修改TranslateExtension
类.这个想法是在幕后它将设置适当的绑定,而不是简单地返回一个值.
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));
}
}
此处的目标是Translator.Instance["Label_Text"]
应该返回当前扩展名为"Label_Text"
返回的翻译.然后,扩展程序应在ProvideValue
方法中设置绑定:
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);
}
}
现在,您需要做的就是每次更改语言后都拨打Translator.Instance.Invalidate()
.
Now all you need to do is to call Translator.Instance.Invalidate()
every time the language is changed.
请注意,使用{i18n:Translate Label_Text}
等同于使用{Binding [Label_Text], Source={x:Static i18n:Translator.Instance}}
,但更为简洁,省去了修改 XAML 文件的工作.
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.
这篇关于重新评估由标记扩展名计算的xaml页中的所有值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!