[英]How can I make a formatted TextBlock width data binding localizable?
在我的WPF应用程序中,我想显示如下所示的内容:
用户Bob已于22:17退出。
“Bob”和“22:17”是数据绑定值。
显而易见的方法是使用带有多个TextBlock
子StackPanel
,其中一些绑定数据:
<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>
。 但这当然不起作用。
这必须是一个常见的问题,对此有什么正确的解决方案?
我看到的问题是你想要一个String
但是在整个String
有不同的UI存在。
一种选择可能是忽略对粗体的需要,只需将单个String
放在Resources.resx文件中即可。 然后,将在TextBlock
绑定的属性中引用该String
,并根据需要返回值; {0}和{1}适用的地方。
另一个选项可能是返回一组要在TextBlock
保存的Run
值。 你不能绑定到3.5中的Run
out,但我相信你可以在4。
<TextBlock>
<Run Text="The user "/><Run FontWeight="Bold" Text="{Binding User}"/><Run Text="has logged at "/><Run FontWeight="Bold" Text="{Binding LogoffTime}"/>
</TextBlock>
最后一个选项可以在这里找到并涉及为Run
概念创建一个更动态的方法,允许您绑定您的值,然后将它们绑定回String
。
我之前从未尝试过这样的事情,但是如果我不得不尝试使用一个转换器来接受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
}
}
这个问题不可能只有一个解决方案,因为它有很多不同的方式可以表现出来,并且有很多不同的方法可以解决它。
如果您是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>
令人惊讶的是,该标记格式也可以在Web浏览器中查看,因此可以使用各种各样的工具和校对进行编辑,而无需使用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>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.