Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Windows App determine if TextBlock is trimmed

I have an GridItem that has a fixed Height/Width.

It contains a textblock that has the max line set.

How can I determine if this text is trimmed? I want to add special functionality if it is trimmed.

like image 466
Frank Sposaro Avatar asked Aug 29 '14 23:08

Frank Sposaro


1 Answers

Old Way - when TextWrapping is set to None

To know if a TextBlock is trimmed, we can subscribe to its SizeChanged event and compare its ActualWidth to the MaxWidth you specified. To get the right ActualWidth of the TextBlock, we will need to leave the TextTrimming to its default value (i.e. TextTrimming.None), and set it to trimmed once the width goes over.

New Way - when TextWrapping is set to Wrap

Now that I know because the TextWrapping is set to Wrap and assume the VirticalAlignment is not specified (default to Stretch), the Width will always stay the same. We only need to monitor the SizeChanged event when the actual height of the TextBlock exceeds the height of its parent.

Let's use a Behavior to encapsulate all the logic above. What needs to be mentioned here is that a static helper class with a bunch of attached properties or a new control that inherits from TextBlock can do exactly the same thing; but being a big Blend fan, I prefer to use Behaviors whenever possible.

The Behavior

public class TextBlockAutoTrimBehavior : DependencyObject, IBehavior
{
    public bool IsTrimmed
    {
        get { return (bool)GetValue(IsTrimmedProperty); }
        set { SetValue(IsTrimmedProperty, value); }
    }

    public static readonly DependencyProperty IsTrimmedProperty =
        DependencyProperty.Register("IsTrimmed", typeof(bool), typeof(TextBlockAutoTrimBehavior), new PropertyMetadata(false));

    public DependencyObject AssociatedObject { get; set; }

    public void Attach(DependencyObject associatedObject)
    {
        this.AssociatedObject = associatedObject;
        var textBlock = (TextBlock)this.AssociatedObject;

        // subscribe to the SizeChanged event so we will know when the Width of the TextBlock goes over the MaxWidth
        textBlock.SizeChanged += TextBlock_SizeChanged;
    }

    private void TextBlock_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        // ignore the first time height change
        if (e.PreviousSize.Height != 0)
        {
            var textBlock = (TextBlock)sender;

            // notify the IsTrimmed dp so your viewmodel property will be notified via data binding
            this.IsTrimmed = true;
            // unsubscribe the event as we don't need it anymore
            textBlock.SizeChanged -= TextBlock_SizeChanged;

            // then we trim the TextBlock
            textBlock.TextTrimming = TextTrimming.WordEllipsis;
        }
    }

    public void Detach()
    {
        var textBlock = (TextBlock)this.AssociatedObject;
        textBlock.SizeChanged += TextBlock_SizeChanged;
    }
}

The XAML

<Grid HorizontalAlignment="Center" Height="73" VerticalAlignment="Center" Width="200" Background="#FFD2A6A6" Margin="628,329,538,366">
    <TextBlock x:Name="MyTextBlock" TextWrapping="Wrap" Text="test" FontSize="29.333">
        <Interactivity:Interaction.Behaviors>
            <local:TextBlockAutoTrimBehavior IsTrimmed="{Binding IsTrimmedInVm}" />
        </Interactivity:Interaction.Behaviors>
    </TextBlock>
</Grid>

Note that the Behavior exposes a dependency property IsTrimmed, you can data bind it to a property in your viewmodel (i.e. IsTrimmedInVm in this case).

P.S. There's no FormattedText function in WinRT otherwise the implementation could be a little bit different.

like image 184
Justin XL Avatar answered Oct 05 '22 12:10

Justin XL