Click here to Skip to main content
15,868,141 members
Articles / Desktop Programming / WPF
Tip/Trick

WPF PathTrimmingTextBlock

Rate me:
Please Sign up or sign in to vote.
4.56/5 (5 votes)
7 Nov 2012CPOL2 min read 44K   682   7   20
How to display a file path in a TextBlock of a GridViewColumn with ellipsis in the center.

Image 1

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... 

XML
<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...

C#
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)


Written By
Software Developer Lovatts Publications
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
SuggestionImproved implementation Pin
User 855309223-Jul-14 15:38
User 855309223-Jul-14 15:38 
GeneralRe: Improved implementation Pin
Member Jaco10-Aug-18 14:03
Member Jaco10-Aug-18 14:03 
GeneralRe: Improved implementation Pin
Dirk Bahle18-May-19 6:56
Dirk Bahle18-May-19 6:56 
Bugusage of wrong Typeface Pin
ujb122-Nov-13 4:27
ujb122-Nov-13 4:27 
QuestionCan't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] Pin
azweepay9-Oct-12 4:32
azweepay9-Oct-12 4:32 
AnswerRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] Pin
Matthew Searles9-Oct-12 15:34
Matthew Searles9-Oct-12 15:34 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] Pin
azweepay9-Oct-12 18:24
azweepay9-Oct-12 18:24 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] Pin
Matthew Searles9-Oct-12 18:37
Matthew Searles9-Oct-12 18:37 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] Pin
azweepay9-Oct-12 18:51
azweepay9-Oct-12 18:51 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] Pin
Matthew Searles9-Oct-12 18:53
Matthew Searles9-Oct-12 18:53 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] Pin
azweepay9-Oct-12 18:56
azweepay9-Oct-12 18:56 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] Pin
Matthew Searles9-Oct-12 19:42
Matthew Searles9-Oct-12 19:42 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] Pin
azweepay9-Oct-12 19:49
azweepay9-Oct-12 19:49 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] Pin
Matthew Searles9-Oct-12 19:51
Matthew Searles9-Oct-12 19:51 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] Pin
azweepay9-Oct-12 19:53
azweepay9-Oct-12 19:53 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] Pin
Matthew Searles9-Oct-12 19:56
Matthew Searles9-Oct-12 19:56 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] Pin
azweepay9-Oct-12 19:58
azweepay9-Oct-12 19:58 
GeneralRe: Can't reproduce the SizeChanged event not firing when decreasing width [inside or outside GridViewColumn] Pin
Matthew Searles9-Oct-12 20:01
Matthew Searles9-Oct-12 20:01 
QuestionWhat about simple converter? Pin
Thornik8-Oct-12 10:44
Thornik8-Oct-12 10:44 
AnswerRe: What about simple converter? Pin
Matthew Searles8-Oct-12 11:17
Matthew Searles8-Oct-12 11:17 

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

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