使用数据绑定值的标记扩展。

13

我正在尝试创建一个WPF MarkupExtension类,该类提供来自我的文本翻译类的翻译文本。翻译功能很好用,但需要使用文本键调用静态方法才能返回翻译后的文本。就像这样:

ImportLabel.Text = Translator.Translate("import files");
// will be "Dateien importieren" in de or "Import files" in en

它的特点是接受计数值以提供更好的措辞。

ImportLabel.Text = Translator.Translate("import n files", FileCount);
// will be "Import 7 files" or "Import 1 file"

另一个例子:如果某个事情需要4分钟,那么它的名称与只需要1分钟时不同。如果文本键“minutes”被定义为任何数字都是“Minuten”,而对于计数为1则为“Minute”,则以下方法调用将返回正确的单词:
Translator.Translate("minutes", numberOfMinutes)
// will be "minute" if it's 1, and "minutes" for anything else

现在,在WPF应用程序中,有很多XAML代码,其中包含大量的文字。为了能够在不疯狂的情况下进行翻译,我需要一个标记扩展,可以传递我的文本键,并在运行时返回已翻译的文本。这部分相当容易。创建一个继承自MarkupExtension的类,添加一个接受文本键作为参数的构造函数,将其存储在私有字段中,并让其ProvideValue方法为存储的键返回翻译文本。
我的真正问题是:如何使我的标记扩展接受计数值,以便它是数据绑定的,并且当计数值更改时,翻译文本也会相应更新?
它应该像这样使用:
<TextBlock Text="{t:Translate 'import files', {Binding FileCount}}"/>

每当FileCount的绑定值发生变化时,TextBlock必须接收新的文本值以反映变化并仍然提供良好的措辞。
我在这里找到了一个看起来类似的解决方案:http://blogs.microsoft.co.il/blogs/tomershamam/archive/2007/10/30/wpf-localization-on-the-fly-language-selection.aspx 但是,尽管我努力跟随它,但我无法理解它做了什么或为什么会起作用。一切似乎都发生在WPF内部,提供的代码只是把它推向正确的方向,但其中的过程不清楚。我无法使其适应我的需求。
我不确定在运行时更改翻译语言是否有用。我认为我需要另一层绑定才能实现这一点。为了保持复杂性低,我不会在基本版本工作之前寻求实现它。
目前没有可以展示给您的代码。它处于一个可怕的状态,唯一的事情就是抛出异常,或者不翻译任何内容。如果在这种情况下存在简单的示例,则非常欢迎。

我认为在这种情况下,IValueConverterMarkupExtension 更适合。 - sa_ddam213
那会是什么样子呢?<TextBlock Text="{Binding FileCount, Converter=???}"/>?有点反向输入。如果我想在运行时使语言字典可变,那也需要一个绑定,对吧? - ygoe
1个回答

17

没关系,我最终弄清楚了参考代码的工作原理并想出了解决方案。这里只是为了记录简单说明一下。

<TextBlock Text="{t:Translate 'import files', {Binding FileCount}}"/>

这需要一个名为TranslateExtension的类,它是从MarkupExtension继承的,并且有一个构造函数,接受两个参数,一个是字符串,一个是Binding。在实例中存储这两个值。该类的ProvideValue方法使用它获得的绑定,向其中添加一个自定义转换器实例,然后返回binding.ProvideValue的结果,该结果是一个BindingExpression实例(如果我没记错)。
public class TranslateExtension : MarkupExtension
{
    public TranslateExtension(string key, Binding countBinding)
    {
        // Save arguments to properties
    }
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        countBinding.Converter = new TranslateConverter(key);
        return countBinding.ProvideValue(serviceProvider);
    }
}

这个转换器的类名为TranslateConverter,它有一个构造函数接受一个参数,类型是字符串。这个参数就是上面TranslateExtension中的key参数,转换器会记住这个参数以备后用。

每当计数值(通过绑定传递)改变时,WPF都会重新请求它的值。它似乎会从绑定的源头,通过转换器,到达表面显示的地方。通过使用转换器,我们完全不必担心绑定,因为转换器将绑定的当前值作为方法参数并期望返回其他内容。输入计数值(int),输出翻译文本(string)。这是我的代码。

因此,转换器的任务是将数字适应为制定的文本。它使用存储的文本键来完成这个任务。所以基本上发生的是一种倒退的数据流。相对于文本键为主要信息,并将计数值添加到其中,我们需要将计数值视为主要信息,并仅使用文本键作为辅助参数来使它完整。这并不是很直观,但绑定需要成为主要触发器。由于键不会改变,因此可以将其永久存储在转换器实例中。每个已转换的文本实例都会得到自己的转换器副本,每个副本都编程了单独的键。

下面是转换器的示例代码:

class TranslateConverter : IValueConverter
{
    private string key;
    public TranslateConverter(string key)
    {
        this.key = key;
    }
    public object Convert(object value, ...)
    {
        return Translator.Translate(key, (int) value);
    }
}

这就是魔力所在。添加错误处理和更多功能以获得解决方案。


1
如果原始绑定已经有转换器,这个代码 出现错误。这里有一个更好的解决方案。 - torvin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接