Click here to Skip to main content
Click here to Skip to main content

WPF PathTrimmingTextBlock

, 7 Nov 2012
Rate this:
Please Sign up or sign in to vote.
How to display a file path in a TextBlock of a GridViewColumn with ellipsis in the center.

Introduction 

Often enough I've had to display a file path in a ListView/GridView, but if the size of the column isn't sufficient, the file part (and some of the directories are clipped). Using TextBlock.TextTrimming doesn't help, it'll simply add an ellipsis to the end of the string. So how do we display a long file path in a GridViewColumn effectively?

Background 

I started off thinking this would be a simple job for a Converter. Simply feed in the Path and TextBlock itself, perform some magic using the FormattedText class, and voila! Turns out it wasn't as simple as that.

The problem is basically that TextBlock.ActualWidth isn't a DependencyProperty, and the effect is, that when the Size of the TextBlock changes, the Converter doesn't recalculate. OK... scrap that. 

Then I thought to myself, well... I'll just derive from TextBlock, create a DependencyProperty called Path, hook SizeChanged, and voila!

Well... it nearly worked.

The SizeChanged event was called when the GridViewColumn Width increased but not when it decreased. Um, what?  

So.. with a little help from nguyentrucdn on MSDN (http://social.msdn.microsoft.com/Forums/en/wpf/thread/8a00e43d-7091-49e7-b57c-86fc0951c4d0), I discovered that the TextBlock won't actually post SizeChanged events in a GridViewColumn when its Width decreases... but it's Container will Smile

So... I thought to myself, well, I'll wrap the TextBlock in a Grid in the XAML, and in the Loaded handler of the TextBlock I'll grab the parent container and hook its SizeChanged event and feed its ActualWidth into the trimming algorithm and voila! Got it! 

Using the code 

Usage is very simple, but with one caveat. You must place the PathTrimmingTextBlock in a Grid or other Container. It'll throw an InvalidOperationException if you don't. Otherwise, it's simply... 

<ListView ItemsSource="{Binding }" HorizontalContentAlignment="Stretch">
    <ListView.ItemContainerStyle>
        <Style TargetType="ListViewItem">
            <Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
        </Style>
    </ListView.ItemContainerStyle>      
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Filename">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>    
                        <Grid>
                            <WpfApplication307:PathTrimmingTextBlock FontSize="40" 
                               Path="{Binding}"></WpfApplication307:PathTrimmingTextBlock>
                        </Grid>
                    </DataTemplate>                            
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
        </GridView>
    </ListView.View>
</ListView> 

Just remember you'll have to reference the correct xmlns, not WpfApplication307. 

The ellipsis algorithm was simple enough. Pass in the required width and full path, split the directory from the filename, remove the last character of the directory until FormattedText tells you its Width is less than Directory + "..." + Filename, and set the Text to that. My implementation looks like...

string GetTrimmedPath(double width)
{            
    string filename = System.IO.Path.GetFileName(Path);
    string directory = System.IO.Path.GetDirectoryName(Path);
    FormattedText formatted;
    bool widthOK = false;
    bool changedWidth = false;

    do
    {
        formatted = new FormattedText(
            "{0}...\\{1}".FormatWith(directory, filename),
            CultureInfo.CurrentCulture,
            FlowDirection.LeftToRight,
            FontFamily.GetTypefaces().First(),
            FontSize,
            Foreground
        );

        widthOK = formatted.Width < width;

        if (!widthOK)
        {
            changedWidth = true;
            directory = directory.Substring(0, directory.Length - 1);
            if (directory.Length == 0) return "...\\" + filename;
        }
    } while (!widthOK);

    if (!changedWidth)
    {
        return Path;
    }

    return "{0}...{1}".FormatWith(directory, filename);
} 

Points of Interest 

That a TextBlock won't post SizeChanged events when its Width decreases... at least if it's a GridViewColumn

I should also note, you don't have to use this in a GridViewColumn, it'll work anywhere really Smile | <img src=   

History 

  • 28/9/2012 - Posted article.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Matthew Searles
Software Developer Lovatts Publications
Australia Australia
No Biography provided

Comments and Discussions

 
SuggestionImproved implementation PinmemberSmorgg23-Jul-14 15:38 
Bugusage of wrong Typeface Pinmemberujb122-Nov-13 4:27 
QuestionCan't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] Pinmemberazweepay9-Oct-12 4:32 
AnswerRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] PinmemberMatthew Searles9-Oct-12 15:34 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] Pinmemberazweepay9-Oct-12 18:24 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] PinmemberMatthew Searles9-Oct-12 18:37 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] Pinmemberazweepay9-Oct-12 18:51 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] PinmemberMatthew Searles9-Oct-12 18:53 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] Pinmemberazweepay9-Oct-12 18:56 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] PinmemberMatthew Searles9-Oct-12 19:42 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] Pinmemberazweepay9-Oct-12 19:49 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] PinmemberMatthew Searles9-Oct-12 19:51 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] Pinmemberazweepay9-Oct-12 19:53 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] PinmemberMatthew Searles9-Oct-12 19:56 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] Pinmemberazweepay9-Oct-12 19:58 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] PinmemberMatthew Searles9-Oct-12 20:01 
QuestionWhat about simple converter? PinmemberThornik8-Oct-12 10:44 
AnswerRe: What about simple converter? [modified] PinmemberMatthew Searles8-Oct-12 11:17 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140827.1 | Last Updated 7 Nov 2012
Article Copyright 2012 by Matthew Searles
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid