重新评估由标记扩展计算的 xaml 页面中的所有值 [英] Re-evaluate all values in xaml page calculated by a markup-extension

查看:15
本文介绍了重新评估由标记扩展计算的 xaml 页面中的所有值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 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:

  1. 您的扩展程序已实例化
  2. ProvideValue 方法在创建的实例上调用,返回值用于目标控件
  3. 未存储对创建的实例的引用(或者是弱引用,我不确定),因此您的扩展已准备好进行 GC
  1. Your extension is instantiated
  2. ProvideValue method is called on the created instance and the returned value is used on the target control
  3. 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

您可以通过定义终结器(析构函数)并在其中设置断点来确认您的扩展仅使用一次.它会在您的页面加载后很快被点击(至少在我的情况下是这样 - 您可能需要显式调用 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆