在我的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>
。但这当然不起作用。
这必须是一个常见的问题,对此有什么正确的解决方案?
答案 0 :(得分:12)
我看到的问题是,您希望单个String
在String
中具有不同的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>