Silverlight(至少从版本4开始)对CharacterEllipsis
没有TextTrimming
选项,WPF has。它可以在TextBlock
上使用。这意味着,如果没有足够的空间来展示“那令人难以置信”,我可以修剪为“那是......”而不是“那太令人难以置信......”我们宁愿这样做。
但是,我们尝试实现自定义文本修剪功能。基本上,那不是那么难。一种非常愚蠢的方法是测量字符串的像素,与可用宽度进行比较,并通过剪切最后一个字符并在文本仍然不适合的循环中添加“...”来操纵字符串。以下是一个如何工作的示例:
// Not perfect but good enough for us
private bool AutoTrim(string fullText, TextBlock textBlock, double maxWidth)
{
double factor = maxWidth / textBlock.ActualWidth;
if (factor > 1)
return false;
int newTextLength = (int)Math.Floor((double)fullText.Length * factor);
string trimTest;
do
{
trimTest = fullText.Substring(0, newTextLength--);
textBlock.Text = trimTest + "..."; // problematic...
factor = maxWidth / textBlock.ActualWidth;
}
while (factor < 1 && newTextLength > 0);
return true;
}
但是在代码后面(或在Behavior
内)执行此操作会导致一些问题:例如,当我们想要更新显示的文本并设置TextBlock的TextBlock1.Text = ...
属性时,它实际上可能会更改我们的viewModel,如果Text绑定到ViewModel属性。出现另一个问题,因为我们注意到视图和viewModel可能由于某种原因而运行同步(我们注意到在ListBox中)。
你对如何以一种好的方式解决这个问题有更好的想法吗?
答案 0 :(得分:5)
Robby Ingebretsen的DynamicTextBox通过将TextBlock包装在自定义控件中并测量可用大小来实现此目的。它匹配WPF的CharacterEllipsis文本修剪模式。 WordEllipsis模式确实被添加到Windows Phone 7 Mango中,但这对此没什么帮助。
答案 1 :(得分:3)
Dan Wahlin在TextTrimming =“WordEllipsis”添加到Silverlight 4之前使用了转换器。您可以在此处找到它:http://weblogs.asp.net/dwahlin/archive/2010/05/05/text-trimming-in-silverlight-4.aspx
答案 2 :(得分:1)
这是我解决缺少CharacterEllipsis选项的方式。我的解决方案也不完美,但到目前为止它对我有用。
首先,我添加了以下帮助方法:
public static void AutoTrimTextBlock(TextBlock textBlock, double maxWidth)
{
if (!string.IsNullOrWhiteSpace(textBlock.Text))
{
var currentWidth = textBlock.ActualWidth;
if (currentWidth > maxWidth)
{
if (textBlock.Text.Length > 2)
{
int substrLength = textBlock.Text.Length - 1;
if (textBlock.Text[substrLength] == '…')
substrLength--;
textBlock.Text = textBlock.Text.Substring(0, substrLength) + '…';
}
else if (textBlock.Text.Length == 2)
{
if (textBlock.Text[1] == '…')
textBlock.Text = "…";
else
textBlock.Text = textBlock.Text[0].ToString() + '…';
}
else //implies: if (length == 1)
{
textBlock.Text = string.Empty;
}
}
}
}
然后我将我的XAML更新为:
<Grid x:Name="MyGrid">
<Grid.ColumnDefinitions>
<ColumnDefinition x:Name="Column0" Width="Auto"/>
<ColumnDefinition x:Name="Column1" Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" x:Name="SomeOtherText" Text="{Binding OtherString}"/>
<TextBlock Grid.Column="1" x:Name="MyTextBlock"
TextWrapping="NoWrap" <!--Disable text wrapping-->
TextTrimming="None" <!--Disable built-in text trimming-->
Text="{Binding MyString, Mode=OneWay}" <!--OneWay binding avoids writing trimmed text back to view model-->
LayoutUpdated="MyTextBlock_LayoutUpdated"/> <!--LayoutUpdated event will trigger custom text trimming-->
</Grid>
最后,在后面的代码中我添加了以下内容:
void MyTextBlock_LayoutUpdated(object sender, System.EventArgs e)
{
// Calculate maximum width for MyTextBlock.
// I did it by checking the parent column width,
// but you can do it any way you like.
double maxWidth = Column1.ActualWidth - MyTextBlock.Margin.Left - MyTextBlock.Margin.Right;
// Start trimming
AutoTrimTextBlock(MyTextBlock, maxWidth);
}
结果:每当MyString属性发生更改时,都会触发LayoutUpdated事件处理程序并调用AutoTrimTextBlock()方法。如果MyTextBlock太宽,它的Text属性会被修剪掉,并且&#39; ...&#39;附加。这会导致另一个LayoutUpdated事件。重复此过程,直到MyTextBlock的宽度小于指定的最大值。
正如我所说,它并不完美,也不是特别优雅,但在上述例子中它可行。
我不喜欢使用LayoutUpdated事件的想法,但我找不到另一个合适的事件。不幸的是,TextBlock不存在TextChanged :(
如果有什么我可以改进的地方,请告诉我。
答案 3 :(得分:0)
private bool TrimExtraCharacters(TextBlock textBlock)
{
if (textBlock != null && textBlock.ActualWidth > 0.1 && !string.IsNullOrWhiteSpace(textBlock.Text))
{
if (textBlock.ActualWidth > textBlock.MaxWidth)
{
textBlock.Text += '…';
int lastLetterIndex = textBlock.Text.Length -2;
do
{
textBlock.Text = textBlock.Text.Remove(lastLetterIndex, 1);
--lastLetterIndex;
} while (textBlock.ActualWidth > textBlock.MaxWidth);
}
return true;
}
return false;
}