如何使格式化的TextBlock宽度数据绑定可本地化?

时间:2010-12-02 18:14:39

标签: wpf binding

在我的WPF应用程序中,我想显示如下内容:

  

用户 Bob 已在 22:17 注销。

“Bob”和“22:17”是数据绑定值。

显而易见的方法是使用StackPanel个多个TextBlock个孩子,其中一些是数据绑定的:

<StackPanel Orientation="Horizontal">
   <TextBlock Text="The user"/>
   <TextBlock Text="{Binding Path=Username}" TextBlock.FontWeight="Bold" />
   <TextBlock Text="has logged off at"/>
   <TextBlock Text="{Binding Path=LogoffTime}" TextBlock.FontWeight="Bold" />
</StackPanel/>

这很有效,但很难看。该程序应该被本地化为不同的语言,并且为“用户”和“已经注销”的单独字符串是本地化灾难的接收者。

理想情况下,我想做这样的事情:

<TextBlock>
     <TextBlock.Text>
         <MultiBinding StringFormat="{}The user <Bold>{0}</Bold> has logged off at <Bold>{1}</Bold>">
             <Binding Path="Username" />
             <Binding Path="LogoffTime" />
         </MultiBinding>
</TextBlock>

所以译者会看到一个完整的句子The user <Bold>{0}</Bold> has logged off at <Bold>{1}</Bold>。但这当然不起作用。

这必须是一个常见的问题,对此有什么正确的解决方案?

3 个答案:

答案 0 :(得分:12)

我看到的问题是,您希望单个StringString中具有不同的UI状态。

一个选项可能是不需要粗体,只需将单个String放在Resources.resx文件中即可。然后,String将在TextBlock绑定的属性中引用,并根据需要返回值; {0}和{1}适用的地方。

另一种选择可能是返回Run中要保留的一组TextBlock值。您无法在3.5中绑定到Run开箱即用,但我相信您可以在4开始。

            <TextBlock>
                  <Run Text="The user "/><Run FontWeight="Bold" Text="{Binding User}"/><Run Text="has logged at "/><Run FontWeight="Bold" Text="{Binding LogoffTime}"/>
            </TextBlock>

最后一个选项可以找到here,并且涉及为Run概念创建一种更动态的方法,允许您绑定您的值,然后将它们绑定回String。< / p>

答案 1 :(得分:4)

我之前从未试图做过这样的事情,但是如果我不得不尝试使用一个转换器来获取MultiBinding并将其分解并返回碎片的StackPanel

例如,绑定类似于:

<Label>
     <Label.Content>
             <MultiBinding Converter={StaticResource TextWithBoldParametersConverter}>
                 <Binding Source="The user {0} has logged off at {1}" />
                 <Binding Path="Username" />
                 <Binding Path="LogoffTime" />
             </MultiBinding>
    </Label.Content>
</Label>

转换器会做类似

的事情
public class TextWithBoldParametersConverter: IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        // Create a StackPanel to hold the content
        // Set StackPanel's Orientation to Horizontal 

        // Take values[0] and split it by the {X} tags

        // Go through array of values parts and create a TextBlock object for each part
            // If the part is an {X} piece, use values[X+1] for Text and make TextBlock bold
            // Add TextBlock to StackPanel

        // return StackPanel
    }
}

答案 2 :(得分:0)

这个问题不太可能有单一的解决方案,因为它有很多不同的方式可以表现出来,并且有很多不同的方法可以解决它。

如果您是Microsoft,解决方案是将所有模板放在资源字典中,创建一个可以使用Expression Blend呈现它们的项目,然后让您的翻译人员使用Blend(可能还有可以提供帮助的人)他们使用它来翻译资源字典中的文本,重新排序模板中按字顺序存在惯用差异的元素。这当然是最昂贵的解决方案,但它的优势在于,在翻译UI的同时解决了格式化问题(比如有人忘记了法语文本占用的空间比英文文本多20%)。它还具有以下优点:它处理UI中的每个文本表示,而不仅仅是通过将文本块堆叠在一起而创建的表示。

如果您真的只需要修复文本块堆栈,则可以创建标记文本的简单XML表示,并使用XSLT从该格式的文件创建XAML。例如,像:

<div id="LogoutTime" class="StackPanel">
   The user 
   <strong><span class="User">Bob</span><strong>
   logged out at
   <strong><span class="Time">22:17</span></strong>
   .
</div>

令人惊讶的是,该标记格式也可以在网络浏览器中查看,因此可以使用各种各样的工具和校对进行编辑,而无需使用Expression Blend。并且转换回XAML相对简单:

<xsl:template match="div[@class='StackPanel']">
   <DataTemplate x:Key="{@id}">
      <StackPanel Orientation="Horizontal">
         <xsl:apply-templates select="node()"/>
      </StackPanel>
   </DataTemplate>
</xsl:template>

<xsl:template match="div/text()">
   <TextBlock Text="{.}"/>
</xsl:template>

<xsl:template match="strong/span">
   <TextBlock FontWeight="Bold">
      <xsl:attribute name="Text">
         <xsl:text>{Binding </xsl:text>
         <xsl:value-of select="@class"/>
         <xsl:text>}</xsl:text>
      </xsl:attribute>
   </TextBlock>
</xsl:template>