TextBlock正在包装什么文本?

时间:2014-04-29 20:02:54

标签: wpf mvvm

我有一个带Text属性的视图模型。视图模型实现了INotifyPropertyChanged。

public string Text
{
    get { return _text; }
    set
    {
        _text = value;
        NotifyPropertyChanged("Text");
    }
}
private string _text;

protected void NotifyPropertyChanged(string propertyName)
{
    if (PropertyChanged != null)
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}

DataTemplate在TextBlock中显示视图模型的Text。文本块已打开包装。

<DataTemplate DataType="{x:Type viewModels:TextViewModel}">
    <Grid Width="{Binding Path=Width, Mode=OneWay}" 
          Height="{Binding Path=Height, Mode=OneWay}">
        <TextBlock Text="{Binding Path=Text, Mode=OneWay}"
                   TextWrapping="Wrap"
                   TextTrimming="CharacterEllipsis"
                   TextAlignment="Left"/>
    </Grid>
</DataTemplate>

我要求显示在另一个视图中修剪的任何文本。例如,如果我的文字是&#34;只是一些文字&#34;只有&#34;只是一些&#34;显示我需要显示&#34; text&#34;在另一种观点。是否有一种简单的方法可以让视图模型确定屏幕上显示的内容,而不知道文本是否显示在带有文本换行的TextBlock中?

我已经看过在视图模型中使用FormattedText类。但是,它需要大量信息,视图模型不具有字体和字体大小。

2 个答案:

答案 0 :(得分:1)

在我看来,viewmodel不是完成它的正确位置。 viewmodel只包含视图的数据。视图显示的是一个观点。如果文本被修剪,则仅显示第一部分。您可以在视图的某些事件处理程序中处理它。其他一些视图可以绑定到同一个视图模型。在另一个视图中,您可以更改仅显示另一方的事件处理程序。

为了减少重复,您可以定义一个包含此功能的用户控件。

无论如何你应该检查你的要求,听起来不是很安静,但这只是我的意见; - )

答案 1 :(得分:0)

@NETscape提供的link向我指出了正确的方向。该链接指向another page,其中讨论了使用attached properties在TextBlock上公开IsTextWrapped属性。我想出了一种暴露修剪指数的类似方法。

我创建了一个静态类来公开一个属性,该属性包含发生包装的索引。另一个属性启用或禁用索引的计算。对于我的情况,我只需要知道在几个场景中发生包装的位置。我正在使用TextBlock的Loaded事件计算HiddenTextIndex。这只能起作用,因为我知道文本不会在视图的上下文中发生变化。我确实需要重构将数据提供给我的视图模型的代码,以确保在最初加载后不需要更改。

public static class TextBlockWrapService
{
    public static readonly DependencyProperty IsWrapAwareProperty =
        DependencyProperty.RegisterAttached("IsWrapAware", typeof(bool), typeof(TextBlockWrapService),
                                            new PropertyMetadata(false, IsWrapAwarePropertyChanged));

    public static readonly DependencyProperty HiddenTextIndexProperty =
        DependencyProperty.RegisterAttached("HiddenTextIndex", typeof(int), typeof(TextBlockWrapService),
                                            new PropertyMetadata(-1));

    public static bool GetIsWrapAware(TextBlock block)
    {
        if (block == null)
            return false;

        return (bool)block.GetValue(IsWrapAwareProperty);
    }

    public static void SetIsWrapAware(TextBlock block, bool value)
    {
        if (block == null)
            return;

        block.SetValue(IsWrapAwareProperty, value);
    }

    public static int GetHiddenTextIndex(TextBlock block)
    {
        if (!GetIsWrapAware(block))
            return -1;

        return (int)block.GetValue(HiddenTextIndexProperty);
    }

    public static void SetHiddenTextIndex(TextBlock block, int value)
    {
        if (block == null)
            return;

        block.SetValue(HiddenTextIndexProperty, value);
    }

    private static void IsWrapAwarePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var block = d as TextBlock;
        if (block == null || e.NewValue == null)
            return;

        bool wrapAware;
        if (!bool.TryParse(e.NewValue.ToString(), out wrapAware))
            return;

        if (wrapAware)
            block.Loaded += TextBlock_Loaded;
        else
            block.Loaded -= TextBlock_Loaded;
    }

    private static void TextBlock_Loaded(object sender, RoutedEventArgs e)
    {
        UpdateHiddenTextIndex(sender as TextBlock);
    }

    private static void UpdateHiddenTextIndex(TextBlock block)
    {
        if (block == null)
            return;

        if (!GetIsWrapAware(block) || block.TextTrimming == TextTrimming.None)
            SetHiddenTextIndex(block, -1);
        else
            SetHiddenTextIndex(block, CalculateHiddenTextIndex(block));
    }

    public static int CalculateHiddenTextIndex(TextBlock block)
    {
        var typeface = new Typeface(block.FontFamily,
                                    block.FontStyle,
                                    block.FontWeight,
                                    block.FontStretch);

        if (string.IsNullOrWhiteSpace(block.Text) ||
            block.ActualHeight <= 1 ||
            !TextIsTrimmedAtLength(block, typeface, block.Text.Length) ||
            TextIsTrimmedAtLength(block, typeface, 0))
            return -1;

        var untrimmedLength = 1;
        var trimmedLength = block.Text.Length;
        while (untrimmedLength < trimmedLength - 1)
        {
            var untestedLength = (trimmedLength + untrimmedLength) / 2;
            if (untestedLength <= untrimmedLength || untestedLength >= trimmedLength)
                break;

            if (TextIsTrimmedAtLength(block, typeface, untestedLength))
                trimmedLength = untestedLength;
            else
                untrimmedLength = untestedLength;
        }

        return untrimmedLength;
    }

    private static bool TextIsTrimmedAtLength(TextBlock block, Typeface typeface, int length)
    {
        var formattedText = new FormattedText(block.Text.Substring(0, length),
                                              Thread.CurrentThread.CurrentCulture,
                                              block.FlowDirection,
                                              typeface,
                                              block.FontSize,
                                              block.Foreground) { MaxTextWidth = block.ActualWidth };
        return formattedText.Height > block.ActualHeight;
    }
}

IsWrapAware将允许我仅在需要时计算索引。 HiddenTextIndex是发生包装的索引。 HiddenTextIndex通过xaml中的绑定传递给视图模型。

<DataTemplate DataType="{x:Type viewModels:TextViewModel}">
    <Grid Width="{Binding Path=Width, Mode=OneWay}"
          Height="{Binding Path=Height, Mode=OneWay}">
        <TextBlock Text="{Binding Path=Text, Mode=OneWay}"
                   TextWrapping="Wrap"
                   TextTrimming="CharacterEllipsis"
                   TextAlignment="Left"
                   utility:TextBlockWrapService.IsWrapAware="True"
                   utility:TextBlockWrapService.HiddenTextIndex="{Binding Path=HiddenTextIndex, Mode=OneWayToSource}"/>
    </Grid>
</DataTemplate>

视图模型上有相应的HiddenTextIndex属性。此时我可以使用Substring来确定隐藏的内容和显示的内容。