Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF XAML show ellipsis if text does not fit in TextBlock

I would like to show ellipsis if my filename and path do not fit in a TextBlock.

For example, if my file name and path are like:

C:\examples\example\folderA\folderB\folderC\myfilename.txt

I would like to show it in my TextBlock like this:

C:\examples...myfilename.txt

UPDATE: I know I can use TextTrimming to set ellipsis at the end. But I need a way to set ellipsis in the middle somewhere.

like image 986
pixel Avatar asked Feb 06 '23 22:02

pixel


2 Answers

Ok, I was interested if this could be done with just basic XAML, and no messing around with measuring or drawing, so started messing around. I don't have time to finish this today, but I thought I'd share as a starting point for you if you'd like to take it and clean it the remaining issue.

The XAML looks like this so far :

<Window.Resources>
    <local:FileNameConverter x:Key="FileNameConverter" />
    <local:FilePathConverter x:Key="FilePathConverter" />

    <Style x:Key="CustomFileText" TargetType="{x:Type TextBox}">
        <Style.Triggers>
            <DataTrigger Binding="{Binding IsKeyboardFocused, RelativeSource={RelativeSource Self}}" Value="false">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="TextBox">
                            <Grid>
                                <TextBox IsHitTestVisible="False" /> <!-- for Border -->
                                <DockPanel>
                                    <TextBlock Text="{TemplateBinding Text, Converter={StaticResource FileNameConverter}}" DockPanel.Dock="Right" Margin="-3,3,4,3" />
                                    <TextBlock Text="{TemplateBinding Text, Converter={StaticResource FilePathConverter}}" TextTrimming="CharacterEllipsis" Margin="4,3,0,3" />
                                </DockPanel>
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</Window.Resources>

<StackPanel Margin="10">
    <TextBox Text="C:\Program Files\Some Directory\SomeFile.txt" Style="{StaticResource CustomFileText}"/>
    <TextBox Margin="0,10" />
</StackPanel>

The end result is that if the TextBox has focus for editing, it displays as a normal TextBox :

enter image description here

But once the user moves their focus elsewhere on the form, it splits up into two separate TextBlocks that uses converters to parse the Directory and FileName. TextTrimming is used on the Directory to give the effect you were describing in your question :

enter image description here

The main problem with this is when resizing, the extra space is added between the two textboxes.

enter image description here

There's two options I can think of here :

  1. Make the template based on a Trigger of some kind that says "only use this template if TextBox.DesiredSize.Width > TextBox.ActualWidth"

  2. Change the XAML in the template so the Name TextBox somehow says "take up all the space you need as your minimum size. If there is extra space, assign it to this box too", while the Directory TextBox says "take up all the space you can, but do not grow bigger than your contents". I am not sure the best way to do this, but I imagine it has something to do with either a different panel, some properties on TextBox I can't think of right now, or some custom converter/binding to limit size.

I am guessing #1 will be easier to implement, but I don't have time to figure it out now. Hope this gives you a good starting point though, and good luck with it! :)

Oh, and converters were just very basic. You'd probably want to add more safeguards, but here's what I was using for testing :

public class FileNameConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value != null && value is string)
            return System.IO.Path.GetFileName((string)value);

        return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

public class FilePathConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value != null && value is string)
            return System.IO.Path.GetDirectoryName((string)value);

        return null;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
like image 121
Rachel Avatar answered Feb 11 '23 18:02

Rachel


I modified XAML provided by Rachel to fix the extra space in the path appearing on window sizing.

    <Window.Resources>
    <local:FileNameConverter x:Key="FileNameConverter" />
    <local:FilePathConverter x:Key="FilePathConverter" />

    <Style x:Key="CustomFileText" TargetType="{x:Type TextBox}">
        <Style.Triggers>
            <DataTrigger Binding="{Binding IsKeyboardFocused, RelativeSource={RelativeSource Self}}" Value="false">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="TextBox">
                            <Grid>
                                <TextBox Grid.ColumnSpan="2" IsHitTestVisible="False"/>
                                <Grid>
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition MaxWidth="{Binding ActualWidth, ElementName=PART_DirMaxWidth}"/>
                                        <ColumnDefinition Width="Auto"/>
                                    </Grid.ColumnDefinitions>
                                    <Canvas x:Name="PART_Canvas">
                                        <TextBlock x:Name="PART_DirMaxWidth" Margin="0" Padding="0" Text="{TemplateBinding Text, Converter={StaticResource FilePathConverter}}" Visibility="Hidden"/>
                                        <TextBlock Width="{Binding ActualWidth, ElementName=PART_Canvas}" Margin="0" Padding="0" Text="{TemplateBinding Text, Converter={StaticResource FilePathConverter}}" TextTrimming="CharacterEllipsis" Background="Transparent"/>
                                    </Canvas>
                                    <StackPanel Grid.Column="1" Orientation="Horizontal">
                                        <TextBlock Margin="0" Padding="0" Text="\" Background="Transparent"/>
                                        <TextBlock Margin="0" Padding="0" Text="{TemplateBinding Text, Converter={StaticResource FileNameConverter}}" Background="Transparent"/>
                                    </StackPanel>
                                </Grid>
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</Window.Resources>
like image 36
edwabr123 Avatar answered Feb 11 '23 20:02

edwabr123