2010-12-02 62 views
13

在我的WPF应用程序,我想展示的东西,看起来像这样:如何使格式化的TextBlock宽度数据绑定可本地化?

用户鲍勃已在22:17注销。

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

最明显的方法,这样做将是使用StackPanelTextBlock孩子,他们中的一些数据绑定:

<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>。但是,这当然不起作用。

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

回答

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 
    } 
} 
12

我看到的问题是,您希望单个StringString之间具有不同的UI存在。

一个选项可以是忽略粗体的需要,只需在Resources.resx文件中放置单个String即可。然后String将在TextBlock所绑定的属性中被引用,并根据需要返回值; {0}和{1}(如适用)。

另一种选择可能是返回一组要在TextBlock内保留的一组Run值。你不能绑定到Run开箱3.5,但是我相信你可以在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

0

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

如果您是微软,解决方案是将所有模板放入资源字典中,创建一个可以使用Expression Blend呈现它们的项目,然后让您的翻译人员使用Blend(可能有人可以帮助他们使用它)来翻译资源字典中的文本,重新排序模板中字元顺序存在惯用差异的元素。这当然是最昂贵的解决方案,但它具有获取格式问题的优势(比如有人忘记了法文文本比英文文本占用了大约20%的空间)并且在翻译UI的同时解决了这个问题。它还具有处理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> 
相关问题