替换Silverlight中缺少TextTrimming选项“CharacterEllipsis”

时间:2011-05-25 15:43:55

标签: silverlight silverlight-4.0 texttrimming

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中)。

你对如何以一种好的方式解决这个问题有更好的想法吗?

4 个答案:

答案 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;
    }