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

查看:42
本文介绍了重新评估由标记扩展名计算的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. 在创建的实例上调用
  3. ProvideValue方法,并在目标控件上使用返回的值
  4. 未存储对创建的实例的引用(或者我不确定是弱引用),因此您的扩展程序已准备就绪,可以用于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

您可以通过定义终结器(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屋!

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