Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

WPF PathTrimmingTextBlock

0.00/5 (No votes)
7 Nov 2012 1  
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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here